redis/pool.go

393 lines
6.6 KiB
Go
Raw Normal View History

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"
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 {
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
2014-05-11 18:11:55 +04:00
Filter(func(*conn) bool)
2013-07-02 15:17:31 +04:00
}
//------------------------------------------------------------------------------
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
2015-01-31 16:20:37 +03:00
usedAt time.Time
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
}
}
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 {
dial func() (*conn, error)
rl *rateLimiter
2013-07-02 15:17:31 +04:00
2015-01-31 16:20:37 +03:00
opt *options
conns 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{
dial: dial,
2014-05-11 18:11:55 +04:00
rl: newRateLimiter(time.Second, 2*opt.PoolSize),
2015-01-31 16:20:37 +03:00
opt: opt,
conns: make(chan *conn, opt.PoolSize),
}
}
func (p *connPool) isClosed() bool { return atomic.LoadInt32(&p.closed) > 0 }
// First available connection, non-blocking
func (p *connPool) first() *conn {
for {
select {
case cn := <-p.conns:
if !cn.isIdle(p.opt.IdleTimeout) {
return cn
}
p.remove(cn)
default:
return nil
}
}
panic("not reached")
}
2013-07-02 15:17:31 +04:00
2015-01-31 16:20:37 +03:00
// Wait for available connection, blocking
func (p *connPool) wait() (*conn, error) {
deadline := time.After(p.opt.PoolTimeout)
for {
select {
case cn := <-p.conns:
if !cn.isIdle(p.opt.IdleTimeout) {
return cn, nil
}
p.remove(cn)
case <-deadline:
return nil, errPoolTimeout
}
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
func (p *connPool) new() (*conn, error) {
2014-10-02 12:32:49 +04:00
if !p.rl.Check() {
2015-01-17 12:56:34 +03:00
err := fmt.Errorf(
"redis: you open connections too fast (last error: %s)",
p.lastDialErr,
)
return nil, err
}
cn, err := p.dial()
if err != nil {
p.lastDialErr = err
}
2015-01-17 12:56:34 +03:00
return cn, err
}
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() {
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
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
cn, err := p.wait()
return cn, false, err
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 {
b, _ := cn.rd.ReadN(cn.rd.Buffered())
2014-07-31 17:01:54 +04:00
log.Printf("redis: connection has unread data: %q", b)
return p.Remove(cn)
2013-09-17 13:03:17 +04:00
}
2015-01-31 16:20:37 +03:00
if p.isClosed() {
return errClosed
}
2014-05-11 18:11:55 +04:00
if p.opt.IdleTimeout > 0 {
cn.usedAt = time.Now()
}
2015-01-31 16:20:37 +03:00
p.conns <- cn
2013-07-02 15:17:31 +04:00
return nil
}
func (p *connPool) Remove(cn *conn) error {
2015-01-31 16:20:37 +03:00
if p.isClosed() {
2013-11-07 18:20:15 +04:00
return nil
}
2015-01-31 16:20:37 +03:00
return p.remove(cn)
2013-07-02 15:17:31 +04:00
}
func (p *connPool) remove(cn *conn) error {
2015-01-31 16:20:37 +03:00
atomic.AddInt32(&p.size, -1)
return cn.Close()
}
// Len returns number of idle connections.
2013-07-02 15:17:31 +04:00
func (p *connPool) Len() int {
2015-01-31 16:20:37 +03:00
return len(p.conns)
2013-07-02 15:17:31 +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
}
2014-05-11 18:11:55 +04:00
func (p *connPool) Filter(f func(*conn) bool) {
2015-01-31 16:20:37 +03:00
for {
select {
case cn := <-p.conns:
if !f(cn) {
p.remove(cn)
}
default:
return
2014-05-11 18:11:55 +04:00
}
}
2015-01-31 16:20:37 +03:00
panic("not reached")
2014-05-11 18:11:55 +04:00
}
2015-01-31 16:20:37 +03:00
func (p *connPool) Close() (err error) {
if !atomic.CompareAndSwapInt32(&p.closed, 0, 1) {
2013-11-07 18:20:15 +04:00
return nil
}
2014-10-02 12:32:49 +04:00
p.rl.Close()
2015-01-31 16:20:37 +03:00
2014-05-11 18:11:55 +04:00
for {
2015-01-31 16:20:37 +03:00
if p.Size() < 1 {
return
2014-05-11 18:11:55 +04:00
}
2015-01-31 16:20:37 +03:00
if e := p.remove(<-p.conns); e != nil {
err = e
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
}
//------------------------------------------------------------------------------
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
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()
}
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()
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")
}
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")
}
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
}
2014-05-11 18:11:55 +04:00
func (p *singleConnPool) Filter(f func(*conn) bool) {
2014-06-28 15:47:37 +04:00
p.cnMtx.Lock()
2014-05-11 18:11:55 +04:00
if p.cn != nil {
if !f(p.cn) {
p.remove()
}
}
2014-06-28 15:47:37 +04:00
p.cnMtx.Unlock()
2014-05-11 18:11:55 +04:00
}
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()
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
}