func(p *ConnPool) removeConn(cn *Conn) { p.connsMu.Lock() for i, c := range p.conns { if c == cn { p.conns = append(p.conns[:i], p.conns[i+1:]...) if cn.pooled { p.poolSize-- p.checkMinIdleConns() } break } } p.connsMu.Unlock() }
// Prune stale connections at the back of the idle list. if p.IdleTimeout > 0 { n := p.idle.count for i := 0; i < n && p.idle.back != nil && p.idle.back.t.Add(p.IdleTimeout).Before(nowFunc()); i++ { pc := p.idle.back p.idle.popBack() p.mu.Unlock() pc.c.Close() p.mu.Lock() p.active-- } }
// Get idle connection from the front of idle list. for p.idle.front != nil { pc := p.idle.front p.idle.popFront() p.mu.Unlock() if (p.TestOnBorrow == nil || p.TestOnBorrow(pc.c, pc.t) == nil) && (p.MaxConnLifetime == 0 || nowFunc().Sub(pc.created) < p.MaxConnLifetime) { return pc, nil } pc.c.Close() p.mu.Lock() p.active-- }
// Check for pool closed before dialing a new connection. if p.closed { p.mu.Unlock() returnnil, errors.New("redigo: get on closed pool") }
var defaultPoolOpts []Opt // if pool size is 0 don't do any pinging, cause there'd be no point if size > 0 { defaultPoolOpts = append(defaultPoolOpts, PingInterval(10*time.Second/time.Duration(size))) }
var po opts for _, opt := rangeappend(defaultPoolOpts, os...) { opt(&po) }
if po.pingInterval > 0 { doEvery(po.pingInterval, func() { // instead of using Cmd/Get, which might make a new connection, // we only check from the pool select { case conn := <-p.pool: // we don't care if PING errors since Put will handle that conn.Cmd("PING") p.Put(conn) default: } }) }
if po.overflowSize > 0 { doEvery(po.overflowDrainInterval, func() { // remove one from the reservePool, if there is any, and try putting it // into the main pool select { case conn := <-p.reservePool: select { case p.pool <- conn: default: // if the main pool is full then just close it conn.Close() } default: } }) }
if size > 0 { // make one connection to make sure the redis instance is actually there if err := mkConn(); err != nil { return &p, err } }
// make the rest of the connections in the background, if any fail it's fine gofunc() { for i := 0; i < size-1; i++ { mkConn() } close(p.initDoneCh) }()
select { case <-p.stopCh: conn.Close() return default: }
select { case p.pool <- conn: default: if p.po.overflowSize == 0 { conn.Close() return }
// we need a separate select here since it's indeterminate which case go // will select and we want to always prefer the main pool over the reserve select { case p.reservePool <- conn: default: conn.Close() } }
// The pool interface type Pool interface { // Get returns a new connection from the pool. Closing the connections puts // it back to the Pool. Closing it when the pool is destroyed or full will // be counted as an error. Get() (interface{}, error)
// Put puts the connection into the pool instead of closing it. Put(interface{}) error
// CloseConn directly close the connection CloseConn(interface{}) error
// ShutDown closes the pool and all its connections. // After ShutDown() the pool is no longer usable. ShutDown() error
// Len returns the current number of connections of the pool. Len() int }
// Configs for pool type Options struct { // The number of the connections when initiate the pool // Also, the least connection number of the pool InitialCap int
// Max connection number in the pool MaxCap int
// Max idle number in the pool MaxIdle int
// The method the build the connection Factory func() (interface{}, error)
// The method to close the connection Close func(interface{})error
// Check connection health Ping func(interface{})error
// Max life time for idle connection IdleTimeout time.Duration
// Max time to get a connection from pool // Else this will return a errs.MaxActiveConnectionErr WaitTimeout time.Duration }
// after one get, current capacity should be lowered by one. if p.Len() != (InitialCap - 1) { t.Errorf("Get error. Expecting %d, got %d", InitialCap-1, p.Len()) } }