2013-07-02 15:17:31 +04:00
|
|
|
package redis
|
|
|
|
|
|
|
|
import (
|
2013-11-07 18:20:15 +04:00
|
|
|
"errors"
|
2015-01-17 12:56:34 +03:00
|
|
|
"fmt"
|
2014-07-31 17:01:54 +04:00
|
|
|
"log"
|
2013-07-02 15:17:31 +04:00
|
|
|
"net"
|
|
|
|
"sync"
|
2015-01-31 16:20:37 +03:00
|
|
|
"sync/atomic"
|
2013-07-02 15:17:31 +04:00
|
|
|
"time"
|
|
|
|
|
2015-03-20 21:26:37 +03:00
|
|
|
"gopkg.in/bsm/ratelimit.v1"
|
2014-06-18 17:47:21 +04:00
|
|
|
"gopkg.in/bufio.v1"
|
2013-07-02 15:17:31 +04:00
|
|
|
)
|
|
|
|
|
2013-11-07 18:20:15 +04:00
|
|
|
var (
|
2015-01-31 16:20:37 +03:00
|
|
|
errClosed = errors.New("redis: client is closed")
|
|
|
|
errPoolTimeout = errors.New("redis: connection pool timeout")
|
2013-11-07 18:20:15 +04:00
|
|
|
)
|
|
|
|
|
2014-05-07 17:03:09 +04:00
|
|
|
var (
|
|
|
|
zeroTime = time.Time{}
|
|
|
|
)
|
|
|
|
|
2013-07-02 15:17:31 +04:00
|
|
|
type pool interface {
|
2015-04-05 15:41:16 +03:00
|
|
|
First() *conn
|
2013-07-02 15:17:31 +04:00
|
|
|
Get() (*conn, bool, error)
|
|
|
|
Put(*conn) error
|
|
|
|
Remove(*conn) error
|
|
|
|
Len() int
|
2013-11-07 18:20:15 +04:00
|
|
|
Size() int
|
2013-07-02 15:17:31 +04:00
|
|
|
Close() error
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
type conn struct {
|
2014-05-11 18:11:55 +04:00
|
|
|
netcn net.Conn
|
2014-06-18 17:47:21 +04:00
|
|
|
rd *bufio.Reader
|
2014-07-13 16:07:37 +04:00
|
|
|
buf []byte
|
2014-03-04 22:40:55 +04:00
|
|
|
|
2015-01-31 16:20:37 +03:00
|
|
|
usedAt time.Time
|
2014-03-04 22:40:55 +04:00
|
|
|
readTimeout time.Duration
|
|
|
|
writeTimeout time.Duration
|
2013-07-02 15:17:31 +04:00
|
|
|
}
|
|
|
|
|
2013-11-07 18:20:15 +04:00
|
|
|
func newConnFunc(dial func() (net.Conn, error)) func() (*conn, error) {
|
|
|
|
return func() (*conn, error) {
|
|
|
|
netcn, err := dial()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
cn := &conn{
|
2014-05-11 18:11:55 +04:00
|
|
|
netcn: netcn,
|
2014-07-13 16:07:37 +04:00
|
|
|
buf: make([]byte, 0, 64),
|
2013-11-07 18:20:15 +04:00
|
|
|
}
|
|
|
|
cn.rd = bufio.NewReader(cn)
|
|
|
|
return cn, nil
|
2013-07-02 15:17:31 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-24 15:12:48 +03:00
|
|
|
func (cn *conn) writeCmds(cmds ...Cmder) error {
|
|
|
|
buf := cn.buf[:0]
|
|
|
|
for _, cmd := range cmds {
|
|
|
|
buf = appendArgs(buf, cmd.args())
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err := cn.Write(buf)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2013-07-02 15:17:31 +04:00
|
|
|
func (cn *conn) Read(b []byte) (int, error) {
|
2013-09-11 19:06:36 +04:00
|
|
|
if cn.readTimeout != 0 {
|
2014-05-11 18:11:55 +04:00
|
|
|
cn.netcn.SetReadDeadline(time.Now().Add(cn.readTimeout))
|
2013-09-11 20:22:10 +04:00
|
|
|
} else {
|
2014-05-11 18:11:55 +04:00
|
|
|
cn.netcn.SetReadDeadline(zeroTime)
|
2013-07-02 15:17:31 +04:00
|
|
|
}
|
2014-05-11 18:11:55 +04:00
|
|
|
return cn.netcn.Read(b)
|
2013-07-02 15:17:31 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (cn *conn) Write(b []byte) (int, error) {
|
2013-09-11 19:06:36 +04:00
|
|
|
if cn.writeTimeout != 0 {
|
2014-05-11 18:11:55 +04:00
|
|
|
cn.netcn.SetWriteDeadline(time.Now().Add(cn.writeTimeout))
|
2013-09-11 20:22:10 +04:00
|
|
|
} else {
|
2014-05-11 18:11:55 +04:00
|
|
|
cn.netcn.SetWriteDeadline(zeroTime)
|
2013-07-02 15:17:31 +04:00
|
|
|
}
|
2014-05-11 18:11:55 +04:00
|
|
|
return cn.netcn.Write(b)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cn *conn) RemoteAddr() net.Addr {
|
|
|
|
return cn.netcn.RemoteAddr()
|
2013-07-02 15:17:31 +04:00
|
|
|
}
|
|
|
|
|
2013-11-04 11:53:48 +04:00
|
|
|
func (cn *conn) Close() error {
|
2014-05-11 18:11:55 +04:00
|
|
|
return cn.netcn.Close()
|
2013-11-04 11:53:48 +04:00
|
|
|
}
|
|
|
|
|
2015-01-31 16:20:37 +03:00
|
|
|
func (cn *conn) isIdle(timeout time.Duration) bool {
|
|
|
|
return timeout > 0 && time.Since(cn.usedAt) > timeout
|
|
|
|
}
|
|
|
|
|
2013-07-02 15:17:31 +04:00
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
type connPool struct {
|
2014-05-15 15:21:37 +04:00
|
|
|
dial func() (*conn, error)
|
2015-03-20 21:26:37 +03:00
|
|
|
rl *ratelimit.RateLimiter
|
2013-07-02 15:17:31 +04:00
|
|
|
|
2015-04-05 15:41:16 +03:00
|
|
|
opt *options
|
|
|
|
freeConns chan *conn
|
2014-05-11 18:11:55 +04:00
|
|
|
|
2015-01-31 16:20:37 +03:00
|
|
|
size int32
|
|
|
|
closed int32
|
2015-01-17 12:56:34 +03:00
|
|
|
|
|
|
|
lastDialErr error
|
2013-07-02 15:17:31 +04:00
|
|
|
}
|
|
|
|
|
2014-05-11 18:11:55 +04:00
|
|
|
func newConnPool(dial func() (*conn, error), opt *options) *connPool {
|
2013-07-02 15:17:31 +04:00
|
|
|
return &connPool{
|
2014-05-15 15:21:37 +04:00
|
|
|
dial: dial,
|
2015-03-20 21:26:37 +03:00
|
|
|
rl: ratelimit.New(2*opt.PoolSize, time.Second),
|
2014-05-11 18:11:55 +04:00
|
|
|
|
2015-04-05 15:41:16 +03:00
|
|
|
opt: opt,
|
|
|
|
freeConns: make(chan *conn, opt.PoolSize),
|
2015-01-31 16:20:37 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *connPool) isClosed() bool { return atomic.LoadInt32(&p.closed) > 0 }
|
|
|
|
|
2015-04-05 15:41:16 +03:00
|
|
|
// First returns first non-idle connection from the pool or nil if
|
|
|
|
// there are no connections.
|
|
|
|
func (p *connPool) First() *conn {
|
2015-01-31 16:20:37 +03:00
|
|
|
for {
|
|
|
|
select {
|
2015-04-05 15:41:16 +03:00
|
|
|
case cn := <-p.freeConns:
|
|
|
|
if cn.isIdle(p.opt.IdleTimeout) {
|
2015-04-17 14:44:56 +03:00
|
|
|
p.Remove(cn)
|
2015-04-05 15:41:16 +03:00
|
|
|
continue
|
2015-01-31 16:20:37 +03:00
|
|
|
}
|
2015-04-05 15:41:16 +03:00
|
|
|
return cn
|
2015-01-31 16:20:37 +03:00
|
|
|
default:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
panic("not reached")
|
|
|
|
}
|
2013-07-02 15:17:31 +04:00
|
|
|
|
2015-04-05 15:41:16 +03:00
|
|
|
// wait waits for free non-idle connection. It returns nil on timeout.
|
|
|
|
func (p *connPool) wait(timeout time.Duration) *conn {
|
|
|
|
deadline := time.After(timeout)
|
2015-01-31 16:20:37 +03:00
|
|
|
for {
|
|
|
|
select {
|
2015-04-05 15:41:16 +03:00
|
|
|
case cn := <-p.freeConns:
|
|
|
|
if cn.isIdle(p.opt.IdleTimeout) {
|
2015-04-17 14:44:56 +03:00
|
|
|
p.Remove(cn)
|
2015-04-05 15:41:16 +03:00
|
|
|
continue
|
2015-01-31 16:20:37 +03:00
|
|
|
}
|
2015-04-05 15:41:16 +03:00
|
|
|
return cn
|
2015-01-31 16:20:37 +03:00
|
|
|
case <-deadline:
|
2015-04-05 15:41:16 +03:00
|
|
|
return nil
|
2015-01-31 16:20:37 +03:00
|
|
|
}
|
2013-07-02 15:17:31 +04:00
|
|
|
}
|
2015-01-31 16:20:37 +03:00
|
|
|
panic("not reached")
|
2013-07-02 15:17:31 +04:00
|
|
|
}
|
|
|
|
|
2015-01-31 16:20:37 +03:00
|
|
|
// Establish a new connection
|
2014-05-15 15:21:37 +04:00
|
|
|
func (p *connPool) new() (*conn, error) {
|
2015-03-20 21:26:37 +03:00
|
|
|
if p.rl.Limit() {
|
2015-01-17 12:56:34 +03:00
|
|
|
err := fmt.Errorf(
|
2015-01-31 15:04:30 +03:00
|
|
|
"redis: you open connections too fast (last error: %v)",
|
2015-01-17 12:56:34 +03:00
|
|
|
p.lastDialErr,
|
|
|
|
)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
cn, err := p.dial()
|
|
|
|
if err != nil {
|
|
|
|
p.lastDialErr = err
|
2014-05-15 15:21:37 +04:00
|
|
|
}
|
2015-01-17 12:56:34 +03:00
|
|
|
return cn, err
|
2014-05-15 15:21:37 +04:00
|
|
|
}
|
|
|
|
|
2015-04-05 15:41:16 +03:00
|
|
|
// Get returns existed connection from the pool or creates a new one
|
|
|
|
// if needed.
|
2013-07-02 15:17:31 +04:00
|
|
|
func (p *connPool) Get() (*conn, bool, error) {
|
2015-01-31 16:20:37 +03:00
|
|
|
if p.isClosed() {
|
2014-01-06 16:31:37 +04:00
|
|
|
return nil, false, errClosed
|
2013-07-02 15:17:31 +04:00
|
|
|
}
|
|
|
|
|
2015-01-31 16:20:37 +03:00
|
|
|
// Fetch first non-idle connection, if available
|
2015-04-05 15:41:16 +03:00
|
|
|
if cn := p.First(); cn != nil {
|
2013-11-07 18:20:15 +04:00
|
|
|
return cn, false, nil
|
|
|
|
}
|
|
|
|
|
2015-01-31 16:20:37 +03:00
|
|
|
// Try to create a new one
|
|
|
|
if ref := atomic.AddInt32(&p.size, 1); int(ref) <= p.opt.PoolSize {
|
2014-06-28 15:47:37 +04:00
|
|
|
cn, err := p.new()
|
2013-07-02 15:17:31 +04:00
|
|
|
if err != nil {
|
2015-01-31 16:20:37 +03:00
|
|
|
atomic.AddInt32(&p.size, -1) // Undo ref increment
|
2013-07-02 15:17:31 +04:00
|
|
|
return nil, false, err
|
|
|
|
}
|
2013-11-07 18:20:15 +04:00
|
|
|
return cn, true, nil
|
2013-07-02 15:17:31 +04:00
|
|
|
}
|
2015-01-31 16:20:37 +03:00
|
|
|
atomic.AddInt32(&p.size, -1)
|
2013-07-02 15:17:31 +04:00
|
|
|
|
2015-01-31 16:20:37 +03:00
|
|
|
// Otherwise, wait for the available connection
|
2015-04-05 15:41:16 +03:00
|
|
|
if cn := p.wait(p.opt.PoolTimeout); cn != nil {
|
|
|
|
return cn, false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, false, errPoolTimeout
|
2013-07-02 15:17:31 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p *connPool) Put(cn *conn) error {
|
2013-09-29 12:11:18 +04:00
|
|
|
if cn.rd.Buffered() != 0 {
|
2014-03-04 22:40:55 +04:00
|
|
|
b, _ := cn.rd.ReadN(cn.rd.Buffered())
|
2014-07-31 17:01:54 +04:00
|
|
|
log.Printf("redis: connection has unread data: %q", b)
|
2014-03-04 22:40:55 +04:00
|
|
|
return p.Remove(cn)
|
2013-09-17 13:03:17 +04:00
|
|
|
}
|
2014-05-11 18:11:55 +04:00
|
|
|
if p.opt.IdleTimeout > 0 {
|
2013-12-30 15:02:14 +04:00
|
|
|
cn.usedAt = time.Now()
|
|
|
|
}
|
2015-04-05 15:41:16 +03:00
|
|
|
p.freeConns <- cn
|
2013-07-02 15:17:31 +04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-02-06 17:30:49 +04:00
|
|
|
func (p *connPool) Remove(cn *conn) error {
|
2015-01-31 16:20:37 +03:00
|
|
|
if p.isClosed() {
|
2015-04-17 14:44:56 +03:00
|
|
|
atomic.AddInt32(&p.size, -1)
|
|
|
|
return cn.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Replace existing connection with new one and unblock `wait`.
|
|
|
|
newcn, err := p.new()
|
|
|
|
if err != nil {
|
|
|
|
atomic.AddInt32(&p.size, -1)
|
|
|
|
} else {
|
|
|
|
p.Put(newcn)
|
2013-11-07 18:20:15 +04:00
|
|
|
}
|
2013-07-02 15:17:31 +04:00
|
|
|
|
2014-02-06 17:30:49 +04:00
|
|
|
return cn.Close()
|
|
|
|
}
|
|
|
|
|
2014-03-04 22:40:55 +04:00
|
|
|
// Len returns number of idle connections.
|
2013-07-02 15:17:31 +04:00
|
|
|
func (p *connPool) Len() int {
|
2015-04-05 15:41:16 +03:00
|
|
|
return len(p.freeConns)
|
2013-07-02 15:17:31 +04:00
|
|
|
}
|
|
|
|
|
2014-03-04 22:40:55 +04:00
|
|
|
// Size returns number of connections in the pool.
|
2013-07-02 15:17:31 +04:00
|
|
|
func (p *connPool) Size() int {
|
2015-01-31 16:20:37 +03:00
|
|
|
return int(atomic.LoadInt32(&p.size))
|
2013-07-02 15:17:31 +04:00
|
|
|
}
|
|
|
|
|
2015-04-05 15:41:16 +03:00
|
|
|
func (p *connPool) Close() (retErr error) {
|
2015-01-31 16:20:37 +03:00
|
|
|
if !atomic.CompareAndSwapInt32(&p.closed, 0, 1) {
|
2013-11-07 18:20:15 +04:00
|
|
|
return nil
|
|
|
|
}
|
2015-01-31 16:20:37 +03:00
|
|
|
|
2015-04-05 15:41:16 +03:00
|
|
|
// Wait until pool has no connections
|
|
|
|
for p.Size() > 0 {
|
|
|
|
cn := p.wait(p.opt.PoolTimeout)
|
|
|
|
if cn == nil {
|
2014-05-11 18:11:55 +04:00
|
|
|
break
|
|
|
|
}
|
2015-04-17 14:44:56 +03:00
|
|
|
if err := p.Remove(cn); err != nil {
|
2015-04-05 15:41:16 +03:00
|
|
|
retErr = err
|
2013-07-02 15:17:31 +04:00
|
|
|
}
|
|
|
|
}
|
2015-04-05 15:41:16 +03:00
|
|
|
|
|
|
|
return retErr
|
2013-07-02 15:17:31 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
type singleConnPool struct {
|
|
|
|
pool pool
|
|
|
|
|
2014-06-28 15:47:37 +04:00
|
|
|
cnMtx sync.Mutex
|
|
|
|
cn *conn
|
|
|
|
|
2013-07-02 15:17:31 +04:00
|
|
|
reusable bool
|
2014-01-06 16:31:37 +04:00
|
|
|
|
|
|
|
closed bool
|
2013-07-02 15:17:31 +04:00
|
|
|
}
|
|
|
|
|
2014-06-28 15:47:37 +04:00
|
|
|
func newSingleConnPool(pool pool, reusable bool) *singleConnPool {
|
2013-07-02 15:17:31 +04:00
|
|
|
return &singleConnPool{
|
|
|
|
pool: pool,
|
|
|
|
reusable: reusable,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-28 15:47:37 +04:00
|
|
|
func (p *singleConnPool) SetConn(cn *conn) {
|
|
|
|
p.cnMtx.Lock()
|
|
|
|
p.cn = cn
|
|
|
|
p.cnMtx.Unlock()
|
|
|
|
}
|
|
|
|
|
2015-04-05 15:41:16 +03:00
|
|
|
func (p *singleConnPool) First() *conn {
|
|
|
|
defer p.cnMtx.Unlock()
|
|
|
|
p.cnMtx.Lock()
|
|
|
|
return p.cn
|
|
|
|
}
|
|
|
|
|
2013-07-02 15:17:31 +04:00
|
|
|
func (p *singleConnPool) Get() (*conn, bool, error) {
|
2014-06-28 15:47:37 +04:00
|
|
|
defer p.cnMtx.Unlock()
|
|
|
|
p.cnMtx.Lock()
|
|
|
|
|
2014-01-06 16:31:37 +04:00
|
|
|
if p.closed {
|
|
|
|
return nil, false, errClosed
|
|
|
|
}
|
2013-07-02 15:17:31 +04:00
|
|
|
if p.cn != nil {
|
|
|
|
return p.cn, false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
cn, isNew, err := p.pool.Get()
|
|
|
|
if err != nil {
|
|
|
|
return nil, false, err
|
|
|
|
}
|
|
|
|
p.cn = cn
|
2014-06-28 15:47:37 +04:00
|
|
|
|
|
|
|
return p.cn, isNew, nil
|
2013-07-02 15:17:31 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p *singleConnPool) Put(cn *conn) error {
|
2014-06-28 15:47:37 +04:00
|
|
|
defer p.cnMtx.Unlock()
|
|
|
|
p.cnMtx.Lock()
|
2013-07-02 15:17:31 +04:00
|
|
|
if p.cn != cn {
|
|
|
|
panic("p.cn != cn")
|
|
|
|
}
|
2014-01-06 16:31:37 +04:00
|
|
|
if p.closed {
|
|
|
|
return errClosed
|
|
|
|
}
|
2013-07-02 15:17:31 +04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-06-28 15:47:37 +04:00
|
|
|
func (p *singleConnPool) put() error {
|
|
|
|
err := p.pool.Put(p.cn)
|
|
|
|
p.cn = nil
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2013-07-02 15:17:31 +04:00
|
|
|
func (p *singleConnPool) Remove(cn *conn) error {
|
2014-06-28 15:47:37 +04:00
|
|
|
defer p.cnMtx.Unlock()
|
|
|
|
p.cnMtx.Lock()
|
2014-05-11 18:11:55 +04:00
|
|
|
if p.cn == nil {
|
|
|
|
panic("p.cn == nil")
|
|
|
|
}
|
2013-07-02 15:17:31 +04:00
|
|
|
if p.cn != cn {
|
|
|
|
panic("p.cn != cn")
|
|
|
|
}
|
2014-01-06 16:31:37 +04:00
|
|
|
if p.closed {
|
|
|
|
return errClosed
|
|
|
|
}
|
2014-05-11 18:11:55 +04:00
|
|
|
return p.remove()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *singleConnPool) remove() error {
|
|
|
|
err := p.pool.Remove(p.cn)
|
2013-07-02 15:17:31 +04:00
|
|
|
p.cn = nil
|
2014-05-11 18:11:55 +04:00
|
|
|
return err
|
2013-07-02 15:17:31 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p *singleConnPool) Len() int {
|
2014-06-28 15:47:37 +04:00
|
|
|
defer p.cnMtx.Unlock()
|
|
|
|
p.cnMtx.Lock()
|
2013-07-02 15:17:31 +04:00
|
|
|
if p.cn == nil {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
2013-11-07 18:20:15 +04:00
|
|
|
func (p *singleConnPool) Size() int {
|
2014-06-28 15:47:37 +04:00
|
|
|
defer p.cnMtx.Unlock()
|
|
|
|
p.cnMtx.Lock()
|
2013-11-07 18:20:15 +04:00
|
|
|
if p.cn == nil {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
2013-07-02 15:17:31 +04:00
|
|
|
func (p *singleConnPool) Close() error {
|
2014-06-28 15:47:37 +04:00
|
|
|
defer p.cnMtx.Unlock()
|
|
|
|
p.cnMtx.Lock()
|
2014-01-06 16:31:37 +04:00
|
|
|
if p.closed {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
p.closed = true
|
2013-07-02 15:17:31 +04:00
|
|
|
var err error
|
|
|
|
if p.cn != nil {
|
|
|
|
if p.reusable {
|
2014-06-28 15:47:37 +04:00
|
|
|
err = p.put()
|
2013-07-02 15:17:31 +04:00
|
|
|
} else {
|
2014-06-28 15:47:37 +04:00
|
|
|
err = p.remove()
|
2013-07-02 15:17:31 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|