зеркало из https://github.com/github/vitess-gh.git
227 строки
5.5 KiB
Go
227 строки
5.5 KiB
Go
|
// Copyright 2015, Google Inc. All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
package tabletserver
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"sync"
|
||
|
"time"
|
||
|
|
||
|
"github.com/youtube/vitess/go/mysql"
|
||
|
"github.com/youtube/vitess/go/mysql/proto"
|
||
|
"github.com/youtube/vitess/go/pools"
|
||
|
"github.com/youtube/vitess/go/stats"
|
||
|
"github.com/youtube/vitess/go/vt/dbconnpool"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
// ErrConnPoolClosed is returned / panicked when the
|
||
|
// connection pool is closed.
|
||
|
ErrConnPoolClosed = errors.New("connection pool is closed")
|
||
|
)
|
||
|
|
||
|
// PoolConn is the interface implemented by users of this specialized pool.
|
||
|
type PoolConn interface {
|
||
|
Exec(query string, maxrows int, wantfields bool, deadline Deadline) (*proto.QueryResult, error)
|
||
|
Stream(query string, callback func(*proto.QueryResult) error, streamBufferSize int) error
|
||
|
Current() string
|
||
|
Close()
|
||
|
Recycle()
|
||
|
Kill()
|
||
|
}
|
||
|
|
||
|
// ConnPool implements a custom connection pool for tabletserver.
|
||
|
// It's similar to dbconnpool.ConnPool, but the connections it creates
|
||
|
// come with built-in ability to kill in-flight queries. These connections
|
||
|
// also trigger a CheckMySQL call if we fail to connect to MySQL.
|
||
|
// Other than the connection type, ConnPool maintains an additional
|
||
|
// pool of dba connections that are used to kill connections.
|
||
|
type ConnPool struct {
|
||
|
mu sync.Mutex
|
||
|
connections *pools.ResourcePool
|
||
|
capacity int
|
||
|
idleTimeout time.Duration
|
||
|
dbaPool *dbconnpool.ConnectionPool
|
||
|
}
|
||
|
|
||
|
// NewConnPool creates a new ConnPool. The name is used
|
||
|
// to publish stats only.
|
||
|
func NewConnPool(name string, capacity int, idleTimeout time.Duration) *ConnPool {
|
||
|
cp := &ConnPool{
|
||
|
capacity: capacity,
|
||
|
idleTimeout: idleTimeout,
|
||
|
dbaPool: dbconnpool.NewConnectionPool("", 1, idleTimeout),
|
||
|
}
|
||
|
if name == "" {
|
||
|
return cp
|
||
|
}
|
||
|
stats.Publish(name+"Capacity", stats.IntFunc(cp.Capacity))
|
||
|
stats.Publish(name+"Available", stats.IntFunc(cp.Available))
|
||
|
stats.Publish(name+"MaxCap", stats.IntFunc(cp.MaxCap))
|
||
|
stats.Publish(name+"WaitCount", stats.IntFunc(cp.WaitCount))
|
||
|
stats.Publish(name+"WaitTime", stats.DurationFunc(cp.WaitTime))
|
||
|
stats.Publish(name+"IdleTimeout", stats.DurationFunc(cp.IdleTimeout))
|
||
|
return cp
|
||
|
}
|
||
|
|
||
|
func (cp *ConnPool) pool() (p *pools.ResourcePool) {
|
||
|
cp.mu.Lock()
|
||
|
p = cp.connections
|
||
|
cp.mu.Unlock()
|
||
|
return p
|
||
|
}
|
||
|
|
||
|
// Open must be called before starting to use the pool.
|
||
|
func (cp *ConnPool) Open(appParams, dbaParams *mysql.ConnectionParams) {
|
||
|
cp.mu.Lock()
|
||
|
defer cp.mu.Unlock()
|
||
|
|
||
|
f := func() (pools.Resource, error) {
|
||
|
return newdbconn(cp, appParams, dbaParams)
|
||
|
}
|
||
|
cp.connections = pools.NewResourcePool(f, cp.capacity, cp.capacity, cp.idleTimeout)
|
||
|
cp.dbaPool.Open(dbconnpool.DBConnectionCreator(dbaParams, mysqlStats))
|
||
|
}
|
||
|
|
||
|
// Close will close the pool and wait for connections to be returned before
|
||
|
// exiting.
|
||
|
func (cp *ConnPool) Close() {
|
||
|
p := cp.pool()
|
||
|
if p == nil {
|
||
|
return
|
||
|
}
|
||
|
// We should not hold the lock while calling Close
|
||
|
// because it waits for connections to be returned.
|
||
|
p.Close()
|
||
|
cp.mu.Lock()
|
||
|
cp.connections = nil
|
||
|
cp.mu.Unlock()
|
||
|
cp.dbaPool.Close()
|
||
|
}
|
||
|
|
||
|
// Get returns a connection.
|
||
|
// You must call Recycle on the PoolConn once done.
|
||
|
func (cp *ConnPool) Get(timeout time.Duration) (PoolConn, error) {
|
||
|
p := cp.pool()
|
||
|
if p == nil {
|
||
|
return nil, ErrConnPoolClosed
|
||
|
}
|
||
|
r, err := p.Get(timeout)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return r.(PoolConn), nil
|
||
|
}
|
||
|
|
||
|
// TryGet returns a connection, or nil.
|
||
|
// You must call Recycle on the PoolConn once done.
|
||
|
func (cp *ConnPool) TryGet() (PoolConn, error) {
|
||
|
p := cp.pool()
|
||
|
if p == nil {
|
||
|
return nil, ErrConnPoolClosed
|
||
|
}
|
||
|
r, err := p.TryGet()
|
||
|
if err != nil || r == nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return r.(PoolConn), nil
|
||
|
}
|
||
|
|
||
|
// Put puts a connection into the pool.
|
||
|
func (cp *ConnPool) Put(conn PoolConn) {
|
||
|
p := cp.pool()
|
||
|
if p == nil {
|
||
|
panic(ErrConnPoolClosed)
|
||
|
}
|
||
|
p.Put(conn)
|
||
|
}
|
||
|
|
||
|
// SetCapacity alters the size of the pool at runtime.
|
||
|
func (cp *ConnPool) SetCapacity(capacity int) (err error) {
|
||
|
cp.mu.Lock()
|
||
|
defer cp.mu.Unlock()
|
||
|
if cp.connections != nil {
|
||
|
err = cp.connections.SetCapacity(capacity)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
cp.capacity = capacity
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// SetIdleTimeout sets the idleTimeout on the pool.
|
||
|
func (cp *ConnPool) SetIdleTimeout(idleTimeout time.Duration) {
|
||
|
cp.mu.Lock()
|
||
|
defer cp.mu.Unlock()
|
||
|
if cp.connections != nil {
|
||
|
cp.connections.SetIdleTimeout(idleTimeout)
|
||
|
}
|
||
|
cp.idleTimeout = idleTimeout
|
||
|
}
|
||
|
|
||
|
// StatsJSON returns the pool stats as a JSOn object.
|
||
|
func (cp *ConnPool) StatsJSON() string {
|
||
|
p := cp.pool()
|
||
|
if p == nil {
|
||
|
return "{}"
|
||
|
}
|
||
|
return p.StatsJSON()
|
||
|
}
|
||
|
|
||
|
// Capacity returns the pool capacity.
|
||
|
func (cp *ConnPool) Capacity() int64 {
|
||
|
p := cp.pool()
|
||
|
if p == nil {
|
||
|
return 0
|
||
|
}
|
||
|
return p.Capacity()
|
||
|
}
|
||
|
|
||
|
// Available returns the number of available connections in the pool
|
||
|
func (cp *ConnPool) Available() int64 {
|
||
|
p := cp.pool()
|
||
|
if p == nil {
|
||
|
return 0
|
||
|
}
|
||
|
return p.Available()
|
||
|
}
|
||
|
|
||
|
// MaxCap returns the maximum size of the pool
|
||
|
func (cp *ConnPool) MaxCap() int64 {
|
||
|
p := cp.pool()
|
||
|
if p == nil {
|
||
|
return 0
|
||
|
}
|
||
|
return p.MaxCap()
|
||
|
}
|
||
|
|
||
|
// WaitCount returns how many clients are waiting for a connection
|
||
|
func (cp *ConnPool) WaitCount() int64 {
|
||
|
p := cp.pool()
|
||
|
if p == nil {
|
||
|
return 0
|
||
|
}
|
||
|
return p.WaitCount()
|
||
|
}
|
||
|
|
||
|
// WaitTime return the pool WaitTime.
|
||
|
func (cp *ConnPool) WaitTime() time.Duration {
|
||
|
p := cp.pool()
|
||
|
if p == nil {
|
||
|
return 0
|
||
|
}
|
||
|
return p.WaitTime()
|
||
|
}
|
||
|
|
||
|
// IdleTimeout returns the idle timeout for the pool.
|
||
|
func (cp *ConnPool) IdleTimeout() time.Duration {
|
||
|
p := cp.pool()
|
||
|
if p == nil {
|
||
|
return 0
|
||
|
}
|
||
|
return p.IdleTimeout()
|
||
|
}
|