mirror of
				https://github.com/redis/go-redis.git
				synced 2025-11-04 02:33:24 +03:00 
			
		
		
		
	Merge pull request #273 from go-redis/fix/extract-pool-package
Extract pool package. Add pool benchmark.
This commit is contained in:
		@@ -2,12 +2,15 @@ package redis_test
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	redigo "github.com/garyburd/redigo/redis"
 | 
						redigo "github.com/garyburd/redigo/redis"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gopkg.in/redis.v3"
 | 
						"gopkg.in/redis.v3"
 | 
				
			||||||
 | 
						"gopkg.in/redis.v3/internal/pool"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func benchmarkRedisClient(poolSize int) *redis.Client {
 | 
					func benchmarkRedisClient(poolSize int) *redis.Client {
 | 
				
			||||||
@@ -274,11 +277,11 @@ func BenchmarkZAdd(b *testing.B) {
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func BenchmarkPool(b *testing.B) {
 | 
					func benchmarkPoolGetPut(b *testing.B, poolSize int) {
 | 
				
			||||||
	client := benchmarkRedisClient(10)
 | 
						dial := func() (*pool.Conn, error) {
 | 
				
			||||||
	defer client.Close()
 | 
							return pool.NewConn(&net.TCPConn{}), nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	pool := client.Pool()
 | 
						pool := pool.NewConnPool(dial, poolSize, time.Second, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	b.ResetTimer()
 | 
						b.ResetTimer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -294,3 +297,49 @@ func BenchmarkPool(b *testing.B) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BenchmarkPoolGetPut10Conns(b *testing.B) {
 | 
				
			||||||
 | 
						benchmarkPoolGetPut(b, 10)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BenchmarkPoolGetPut100Conns(b *testing.B) {
 | 
				
			||||||
 | 
						benchmarkPoolGetPut(b, 100)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BenchmarkPoolGetPut1000Conns(b *testing.B) {
 | 
				
			||||||
 | 
						benchmarkPoolGetPut(b, 1000)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func benchmarkPoolGetRemove(b *testing.B, poolSize int) {
 | 
				
			||||||
 | 
						dial := func() (*pool.Conn, error) {
 | 
				
			||||||
 | 
							return pool.NewConn(&net.TCPConn{}), nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pool := pool.NewConnPool(dial, poolSize, time.Second, 0)
 | 
				
			||||||
 | 
						removeReason := errors.New("benchmark")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.ResetTimer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.RunParallel(func(pb *testing.PB) {
 | 
				
			||||||
 | 
							for pb.Next() {
 | 
				
			||||||
 | 
								conn, _, err := pool.Get()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									b.Fatalf("no error expected on pool.Get but received: %s", err.Error())
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if err = pool.Remove(conn, removeReason); err != nil {
 | 
				
			||||||
 | 
									b.Fatalf("no error expected on pool.Remove but received: %s", err.Error())
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BenchmarkPoolGetRemove10Conns(b *testing.B) {
 | 
				
			||||||
 | 
						benchmarkPoolGetRemove(b, 10)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BenchmarkPoolGetRemove100Conns(b *testing.B) {
 | 
				
			||||||
 | 
						benchmarkPoolGetRemove(b, 100)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BenchmarkPoolGetRemove1000Conns(b *testing.B) {
 | 
				
			||||||
 | 
						benchmarkPoolGetRemove(b, 1000)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ package redis
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"gopkg.in/redis.v3/internal/hashtag"
 | 
						"gopkg.in/redis.v3/internal/hashtag"
 | 
				
			||||||
 | 
						"gopkg.in/redis.v3/internal/pool"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ClusterPipeline is not thread-safe.
 | 
					// ClusterPipeline is not thread-safe.
 | 
				
			||||||
@@ -96,9 +97,9 @@ func (pipe *ClusterPipeline) Close() error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (pipe *ClusterPipeline) execClusterCmds(
 | 
					func (pipe *ClusterPipeline) execClusterCmds(
 | 
				
			||||||
	cn *conn, cmds []Cmder, failedCmds map[string][]Cmder,
 | 
						cn *pool.Conn, cmds []Cmder, failedCmds map[string][]Cmder,
 | 
				
			||||||
) (map[string][]Cmder, error) {
 | 
					) (map[string][]Cmder, error) {
 | 
				
			||||||
	if err := cn.writeCmds(cmds...); err != nil {
 | 
						if err := writeCmd(cn, cmds...); err != nil {
 | 
				
			||||||
		setCmdsErr(cmds, err)
 | 
							setCmdsErr(cmds, err)
 | 
				
			||||||
		return failedCmds, err
 | 
							return failedCmds, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										56
									
								
								command.go
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								command.go
									
									
									
									
									
								
							@@ -6,6 +6,8 @@ import (
 | 
				
			|||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gopkg.in/redis.v3/internal/pool"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
@@ -28,7 +30,7 @@ var (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type Cmder interface {
 | 
					type Cmder interface {
 | 
				
			||||||
	args() []interface{}
 | 
						args() []interface{}
 | 
				
			||||||
	readReply(*conn) error
 | 
						readReply(*pool.Conn) error
 | 
				
			||||||
	setErr(error)
 | 
						setErr(error)
 | 
				
			||||||
	reset()
 | 
						reset()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -51,6 +53,20 @@ func resetCmds(cmds []Cmder) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func writeCmd(cn *pool.Conn, cmds ...Cmder) error {
 | 
				
			||||||
 | 
						cn.Buf = cn.Buf[:0]
 | 
				
			||||||
 | 
						for _, cmd := range cmds {
 | 
				
			||||||
 | 
							var err error
 | 
				
			||||||
 | 
							cn.Buf, err = appendArgs(cn.Buf, cmd.args())
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err := cn.Write(cn.Buf)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func cmdString(cmd Cmder, val interface{}) string {
 | 
					func cmdString(cmd Cmder, val interface{}) string {
 | 
				
			||||||
	var ss []string
 | 
						var ss []string
 | 
				
			||||||
	for _, arg := range cmd.args() {
 | 
						for _, arg := range cmd.args() {
 | 
				
			||||||
@@ -143,7 +159,7 @@ func (cmd *Cmd) String() string {
 | 
				
			|||||||
	return cmdString(cmd, cmd.val)
 | 
						return cmdString(cmd, cmd.val)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (cmd *Cmd) readReply(cn *conn) error {
 | 
					func (cmd *Cmd) readReply(cn *pool.Conn) error {
 | 
				
			||||||
	val, err := readReply(cn, sliceParser)
 | 
						val, err := readReply(cn, sliceParser)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		cmd.err = err
 | 
							cmd.err = err
 | 
				
			||||||
@@ -188,7 +204,7 @@ func (cmd *SliceCmd) String() string {
 | 
				
			|||||||
	return cmdString(cmd, cmd.val)
 | 
						return cmdString(cmd, cmd.val)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (cmd *SliceCmd) readReply(cn *conn) error {
 | 
					func (cmd *SliceCmd) readReply(cn *pool.Conn) error {
 | 
				
			||||||
	v, err := readArrayReply(cn, sliceParser)
 | 
						v, err := readArrayReply(cn, sliceParser)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		cmd.err = err
 | 
							cmd.err = err
 | 
				
			||||||
@@ -231,7 +247,7 @@ func (cmd *StatusCmd) String() string {
 | 
				
			|||||||
	return cmdString(cmd, cmd.val)
 | 
						return cmdString(cmd, cmd.val)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (cmd *StatusCmd) readReply(cn *conn) error {
 | 
					func (cmd *StatusCmd) readReply(cn *pool.Conn) error {
 | 
				
			||||||
	cmd.val, cmd.err = readStringReply(cn)
 | 
						cmd.val, cmd.err = readStringReply(cn)
 | 
				
			||||||
	return cmd.err
 | 
						return cmd.err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -265,7 +281,7 @@ func (cmd *IntCmd) String() string {
 | 
				
			|||||||
	return cmdString(cmd, cmd.val)
 | 
						return cmdString(cmd, cmd.val)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (cmd *IntCmd) readReply(cn *conn) error {
 | 
					func (cmd *IntCmd) readReply(cn *pool.Conn) error {
 | 
				
			||||||
	cmd.val, cmd.err = readIntReply(cn)
 | 
						cmd.val, cmd.err = readIntReply(cn)
 | 
				
			||||||
	return cmd.err
 | 
						return cmd.err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -303,7 +319,7 @@ func (cmd *DurationCmd) String() string {
 | 
				
			|||||||
	return cmdString(cmd, cmd.val)
 | 
						return cmdString(cmd, cmd.val)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (cmd *DurationCmd) readReply(cn *conn) error {
 | 
					func (cmd *DurationCmd) readReply(cn *pool.Conn) error {
 | 
				
			||||||
	n, err := readIntReply(cn)
 | 
						n, err := readIntReply(cn)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		cmd.err = err
 | 
							cmd.err = err
 | 
				
			||||||
@@ -344,7 +360,7 @@ func (cmd *BoolCmd) String() string {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
var ok = []byte("OK")
 | 
					var ok = []byte("OK")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (cmd *BoolCmd) readReply(cn *conn) error {
 | 
					func (cmd *BoolCmd) readReply(cn *pool.Conn) error {
 | 
				
			||||||
	v, err := readReply(cn, nil)
 | 
						v, err := readReply(cn, nil)
 | 
				
			||||||
	// `SET key value NX` returns nil when key already exists. But
 | 
						// `SET key value NX` returns nil when key already exists. But
 | 
				
			||||||
	// `SETNX key value` returns bool (0/1). So convert nil to bool.
 | 
						// `SETNX key value` returns bool (0/1). So convert nil to bool.
 | 
				
			||||||
@@ -430,13 +446,17 @@ func (cmd *StringCmd) String() string {
 | 
				
			|||||||
	return cmdString(cmd, cmd.val)
 | 
						return cmdString(cmd, cmd.val)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (cmd *StringCmd) readReply(cn *conn) error {
 | 
					func (cmd *StringCmd) readReply(cn *pool.Conn) error {
 | 
				
			||||||
	b, err := readBytesReply(cn)
 | 
						b, err := readBytesReply(cn)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		cmd.err = err
 | 
							cmd.err = err
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	cmd.val = cn.copyBuf(b)
 | 
					
 | 
				
			||||||
 | 
						new := make([]byte, len(b))
 | 
				
			||||||
 | 
						copy(new, b)
 | 
				
			||||||
 | 
						cmd.val = new
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -469,7 +489,7 @@ func (cmd *FloatCmd) String() string {
 | 
				
			|||||||
	return cmdString(cmd, cmd.val)
 | 
						return cmdString(cmd, cmd.val)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (cmd *FloatCmd) readReply(cn *conn) error {
 | 
					func (cmd *FloatCmd) readReply(cn *pool.Conn) error {
 | 
				
			||||||
	cmd.val, cmd.err = readFloatReply(cn)
 | 
						cmd.val, cmd.err = readFloatReply(cn)
 | 
				
			||||||
	return cmd.err
 | 
						return cmd.err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -503,7 +523,7 @@ func (cmd *StringSliceCmd) String() string {
 | 
				
			|||||||
	return cmdString(cmd, cmd.val)
 | 
						return cmdString(cmd, cmd.val)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (cmd *StringSliceCmd) readReply(cn *conn) error {
 | 
					func (cmd *StringSliceCmd) readReply(cn *pool.Conn) error {
 | 
				
			||||||
	v, err := readArrayReply(cn, stringSliceParser)
 | 
						v, err := readArrayReply(cn, stringSliceParser)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		cmd.err = err
 | 
							cmd.err = err
 | 
				
			||||||
@@ -542,7 +562,7 @@ func (cmd *BoolSliceCmd) String() string {
 | 
				
			|||||||
	return cmdString(cmd, cmd.val)
 | 
						return cmdString(cmd, cmd.val)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (cmd *BoolSliceCmd) readReply(cn *conn) error {
 | 
					func (cmd *BoolSliceCmd) readReply(cn *pool.Conn) error {
 | 
				
			||||||
	v, err := readArrayReply(cn, boolSliceParser)
 | 
						v, err := readArrayReply(cn, boolSliceParser)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		cmd.err = err
 | 
							cmd.err = err
 | 
				
			||||||
@@ -581,7 +601,7 @@ func (cmd *StringStringMapCmd) String() string {
 | 
				
			|||||||
	return cmdString(cmd, cmd.val)
 | 
						return cmdString(cmd, cmd.val)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (cmd *StringStringMapCmd) readReply(cn *conn) error {
 | 
					func (cmd *StringStringMapCmd) readReply(cn *pool.Conn) error {
 | 
				
			||||||
	v, err := readArrayReply(cn, stringStringMapParser)
 | 
						v, err := readArrayReply(cn, stringStringMapParser)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		cmd.err = err
 | 
							cmd.err = err
 | 
				
			||||||
@@ -620,7 +640,7 @@ func (cmd *StringIntMapCmd) reset() {
 | 
				
			|||||||
	cmd.err = nil
 | 
						cmd.err = nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (cmd *StringIntMapCmd) readReply(cn *conn) error {
 | 
					func (cmd *StringIntMapCmd) readReply(cn *pool.Conn) error {
 | 
				
			||||||
	v, err := readArrayReply(cn, stringIntMapParser)
 | 
						v, err := readArrayReply(cn, stringIntMapParser)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		cmd.err = err
 | 
							cmd.err = err
 | 
				
			||||||
@@ -659,7 +679,7 @@ func (cmd *ZSliceCmd) String() string {
 | 
				
			|||||||
	return cmdString(cmd, cmd.val)
 | 
						return cmdString(cmd, cmd.val)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (cmd *ZSliceCmd) readReply(cn *conn) error {
 | 
					func (cmd *ZSliceCmd) readReply(cn *pool.Conn) error {
 | 
				
			||||||
	v, err := readArrayReply(cn, zSliceParser)
 | 
						v, err := readArrayReply(cn, zSliceParser)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		cmd.err = err
 | 
							cmd.err = err
 | 
				
			||||||
@@ -703,7 +723,7 @@ func (cmd *ScanCmd) String() string {
 | 
				
			|||||||
	return cmdString(cmd, cmd.keys)
 | 
						return cmdString(cmd, cmd.keys)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (cmd *ScanCmd) readReply(cn *conn) error {
 | 
					func (cmd *ScanCmd) readReply(cn *pool.Conn) error {
 | 
				
			||||||
	keys, cursor, err := readScanReply(cn)
 | 
						keys, cursor, err := readScanReply(cn)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		cmd.err = err
 | 
							cmd.err = err
 | 
				
			||||||
@@ -751,7 +771,7 @@ func (cmd *ClusterSlotCmd) reset() {
 | 
				
			|||||||
	cmd.err = nil
 | 
						cmd.err = nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (cmd *ClusterSlotCmd) readReply(cn *conn) error {
 | 
					func (cmd *ClusterSlotCmd) readReply(cn *pool.Conn) error {
 | 
				
			||||||
	v, err := readArrayReply(cn, clusterSlotInfoSliceParser)
 | 
						v, err := readArrayReply(cn, clusterSlotInfoSliceParser)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		cmd.err = err
 | 
							cmd.err = err
 | 
				
			||||||
@@ -838,7 +858,7 @@ func (cmd *GeoLocationCmd) String() string {
 | 
				
			|||||||
	return cmdString(cmd, cmd.locations)
 | 
						return cmdString(cmd, cmd.locations)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (cmd *GeoLocationCmd) readReply(cn *conn) error {
 | 
					func (cmd *GeoLocationCmd) readReply(cn *pool.Conn) error {
 | 
				
			||||||
	reply, err := readArrayReply(cn, newGeoLocationSliceParser(cmd.q))
 | 
						reply, err := readArrayReply(cn, newGeoLocationSliceParser(cmd.q))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		cmd.err = err
 | 
							cmd.err = err
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										120
									
								
								conn.go
									
									
									
									
									
								
							
							
						
						
									
										120
									
								
								conn.go
									
									
									
									
									
								
							@@ -1,120 +0,0 @@
 | 
				
			|||||||
package redis
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"bufio"
 | 
					 | 
				
			||||||
	"net"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const defaultBufSize = 4096
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var noTimeout = time.Time{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Stubbed in tests.
 | 
					 | 
				
			||||||
var now = time.Now
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type conn struct {
 | 
					 | 
				
			||||||
	netcn net.Conn
 | 
					 | 
				
			||||||
	rd    *bufio.Reader
 | 
					 | 
				
			||||||
	buf   []byte
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	UsedAt       time.Time
 | 
					 | 
				
			||||||
	ReadTimeout  time.Duration
 | 
					 | 
				
			||||||
	WriteTimeout time.Duration
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func newConnDialer(opt *Options) func() (*conn, error) {
 | 
					 | 
				
			||||||
	dialer := opt.getDialer()
 | 
					 | 
				
			||||||
	return func() (*conn, error) {
 | 
					 | 
				
			||||||
		netcn, err := dialer()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return nil, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		cn := &conn{
 | 
					 | 
				
			||||||
			netcn: netcn,
 | 
					 | 
				
			||||||
			buf:   make([]byte, defaultBufSize),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			UsedAt: now(),
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		cn.rd = bufio.NewReader(cn)
 | 
					 | 
				
			||||||
		return cn, cn.init(opt)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (cn *conn) init(opt *Options) error {
 | 
					 | 
				
			||||||
	if opt.Password == "" && opt.DB == 0 {
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Temp client for Auth and Select.
 | 
					 | 
				
			||||||
	client := newClient(opt, newSingleConnPool(cn))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if opt.Password != "" {
 | 
					 | 
				
			||||||
		if err := client.Auth(opt.Password).Err(); err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if opt.DB > 0 {
 | 
					 | 
				
			||||||
		if err := client.Select(opt.DB).Err(); err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (cn *conn) writeCmds(cmds ...Cmder) error {
 | 
					 | 
				
			||||||
	cn.buf = cn.buf[:0]
 | 
					 | 
				
			||||||
	for _, cmd := range cmds {
 | 
					 | 
				
			||||||
		var err error
 | 
					 | 
				
			||||||
		cn.buf, err = appendArgs(cn.buf, cmd.args())
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_, err := cn.Write(cn.buf)
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (cn *conn) Read(b []byte) (int, error) {
 | 
					 | 
				
			||||||
	cn.UsedAt = now()
 | 
					 | 
				
			||||||
	if cn.ReadTimeout != 0 {
 | 
					 | 
				
			||||||
		cn.netcn.SetReadDeadline(cn.UsedAt.Add(cn.ReadTimeout))
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		cn.netcn.SetReadDeadline(noTimeout)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return cn.netcn.Read(b)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (cn *conn) Write(b []byte) (int, error) {
 | 
					 | 
				
			||||||
	cn.UsedAt = now()
 | 
					 | 
				
			||||||
	if cn.WriteTimeout != 0 {
 | 
					 | 
				
			||||||
		cn.netcn.SetWriteDeadline(cn.UsedAt.Add(cn.WriteTimeout))
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		cn.netcn.SetWriteDeadline(noTimeout)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return cn.netcn.Write(b)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (cn *conn) RemoteAddr() net.Addr {
 | 
					 | 
				
			||||||
	return cn.netcn.RemoteAddr()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (cn *conn) Close() error {
 | 
					 | 
				
			||||||
	return cn.netcn.Close()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func isSameSlice(s1, s2 []byte) bool {
 | 
					 | 
				
			||||||
	return len(s1) > 0 && len(s2) > 0 && &s1[0] == &s2[0]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (cn *conn) copyBuf(b []byte) []byte {
 | 
					 | 
				
			||||||
	if isSameSlice(b, cn.buf) {
 | 
					 | 
				
			||||||
		new := make([]byte, len(b))
 | 
					 | 
				
			||||||
		copy(new, b)
 | 
					 | 
				
			||||||
		return new
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return b
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										25
									
								
								conn_test.go
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								conn_test.go
									
									
									
									
									
								
							@@ -1,25 +0,0 @@
 | 
				
			|||||||
package redis_test
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"net"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	. "github.com/onsi/ginkgo"
 | 
					 | 
				
			||||||
	. "github.com/onsi/gomega"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"gopkg.in/redis.v3"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _ = Describe("newConnDialer with bad connection", func() {
 | 
					 | 
				
			||||||
	It("should return an error", func() {
 | 
					 | 
				
			||||||
		dialer := redis.NewConnDialer(&redis.Options{
 | 
					 | 
				
			||||||
			Dialer: func() (net.Conn, error) {
 | 
					 | 
				
			||||||
				return &badConn{}, nil
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			MaxRetries: 3,
 | 
					 | 
				
			||||||
			Password:   "password",
 | 
					 | 
				
			||||||
			DB:         1,
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
		_, err := dialer()
 | 
					 | 
				
			||||||
		Expect(err).To(MatchError("bad connection"))
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
							
								
								
									
										3
									
								
								error.go
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								error.go
									
									
									
									
									
								
							@@ -1,12 +1,15 @@
 | 
				
			|||||||
package redis
 | 
					package redis
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var errClosed = errors.New("redis: client is closed")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Redis nil reply, .e.g. when key does not exist.
 | 
					// Redis nil reply, .e.g. when key does not exist.
 | 
				
			||||||
var Nil = errorf("redis: nil")
 | 
					var Nil = errorf("redis: nil")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,30 +1,11 @@
 | 
				
			|||||||
package redis
 | 
					package redis
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import "gopkg.in/redis.v3/internal/pool"
 | 
				
			||||||
	"net"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *baseClient) Pool() pool {
 | 
					func (c *baseClient) Pool() pool.Pooler {
 | 
				
			||||||
	return c.connPool
 | 
						return c.connPool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *PubSub) Pool() pool {
 | 
					func (c *PubSub) Pool() pool.Pooler {
 | 
				
			||||||
	return c.base.connPool
 | 
						return c.base.connPool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
var NewConnDialer = newConnDialer
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (cn *conn) SetNetConn(netcn net.Conn) {
 | 
					 | 
				
			||||||
	cn.netcn = netcn
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func SetTime(tm time.Time) {
 | 
					 | 
				
			||||||
	now = func() time.Time {
 | 
					 | 
				
			||||||
		return tm
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func RestoreTime() {
 | 
					 | 
				
			||||||
	now = time.Now
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										60
									
								
								internal/pool/conn.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								internal/pool/conn.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					package pool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bufio"
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const defaultBufSize = 4096
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var noTimeout = time.Time{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Conn struct {
 | 
				
			||||||
 | 
						NetConn net.Conn
 | 
				
			||||||
 | 
						Rd      *bufio.Reader
 | 
				
			||||||
 | 
						Buf     []byte
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						UsedAt       time.Time
 | 
				
			||||||
 | 
						ReadTimeout  time.Duration
 | 
				
			||||||
 | 
						WriteTimeout time.Duration
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewConn(netConn net.Conn) *Conn {
 | 
				
			||||||
 | 
						cn := &Conn{
 | 
				
			||||||
 | 
							NetConn: netConn,
 | 
				
			||||||
 | 
							Buf:     make([]byte, defaultBufSize),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							UsedAt: time.Now(),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						cn.Rd = bufio.NewReader(cn)
 | 
				
			||||||
 | 
						return cn
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (cn *Conn) Read(b []byte) (int, error) {
 | 
				
			||||||
 | 
						cn.UsedAt = time.Now()
 | 
				
			||||||
 | 
						if cn.ReadTimeout != 0 {
 | 
				
			||||||
 | 
							cn.NetConn.SetReadDeadline(cn.UsedAt.Add(cn.ReadTimeout))
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							cn.NetConn.SetReadDeadline(noTimeout)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return cn.NetConn.Read(b)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (cn *Conn) Write(b []byte) (int, error) {
 | 
				
			||||||
 | 
						cn.UsedAt = time.Now()
 | 
				
			||||||
 | 
						if cn.WriteTimeout != 0 {
 | 
				
			||||||
 | 
							cn.NetConn.SetWriteDeadline(cn.UsedAt.Add(cn.WriteTimeout))
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							cn.NetConn.SetWriteDeadline(noTimeout)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return cn.NetConn.Write(b)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (cn *Conn) RemoteAddr() net.Addr {
 | 
				
			||||||
 | 
						return cn.NetConn.RemoteAddr()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (cn *Conn) Close() error {
 | 
				
			||||||
 | 
						return cn.NetConn.Close()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										100
									
								
								internal/pool/conn_list.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								internal/pool/conn_list.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,100 @@
 | 
				
			|||||||
 | 
					package pool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
						"sync/atomic"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type connList struct {
 | 
				
			||||||
 | 
						cns  []*Conn
 | 
				
			||||||
 | 
						mx   sync.Mutex
 | 
				
			||||||
 | 
						len  int32 // atomic
 | 
				
			||||||
 | 
						size int32
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newConnList(size int) *connList {
 | 
				
			||||||
 | 
						return &connList{
 | 
				
			||||||
 | 
							cns:  make([]*Conn, 0, size),
 | 
				
			||||||
 | 
							size: int32(size),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l *connList) Len() int {
 | 
				
			||||||
 | 
						return int(atomic.LoadInt32(&l.len))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Reserve reserves place in the list and returns true on success. The
 | 
				
			||||||
 | 
					// caller must add or remove connection if place was reserved.
 | 
				
			||||||
 | 
					func (l *connList) Reserve() bool {
 | 
				
			||||||
 | 
						len := atomic.AddInt32(&l.len, 1)
 | 
				
			||||||
 | 
						reserved := len <= l.size
 | 
				
			||||||
 | 
						if !reserved {
 | 
				
			||||||
 | 
							atomic.AddInt32(&l.len, -1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return reserved
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Add adds connection to the list. The caller must reserve place first.
 | 
				
			||||||
 | 
					func (l *connList) Add(cn *Conn) {
 | 
				
			||||||
 | 
						l.mx.Lock()
 | 
				
			||||||
 | 
						l.cns = append(l.cns, cn)
 | 
				
			||||||
 | 
						l.mx.Unlock()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Remove closes connection and removes it from the list.
 | 
				
			||||||
 | 
					func (l *connList) Remove(cn *Conn) error {
 | 
				
			||||||
 | 
						defer l.mx.Unlock()
 | 
				
			||||||
 | 
						l.mx.Lock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if cn == nil {
 | 
				
			||||||
 | 
							atomic.AddInt32(&l.len, -1)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i, c := range l.cns {
 | 
				
			||||||
 | 
							if c == cn {
 | 
				
			||||||
 | 
								l.cns = append(l.cns[:i], l.cns[i+1:]...)
 | 
				
			||||||
 | 
								atomic.AddInt32(&l.len, -1)
 | 
				
			||||||
 | 
								return cn.Close()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if l.closed() {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						panic("conn not found in the list")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l *connList) Replace(cn, newcn *Conn) error {
 | 
				
			||||||
 | 
						defer l.mx.Unlock()
 | 
				
			||||||
 | 
						l.mx.Lock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i, c := range l.cns {
 | 
				
			||||||
 | 
							if c == cn {
 | 
				
			||||||
 | 
								l.cns[i] = newcn
 | 
				
			||||||
 | 
								return cn.Close()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if l.closed() {
 | 
				
			||||||
 | 
							return newcn.Close()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						panic("conn not found in the list")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l *connList) Close() (retErr error) {
 | 
				
			||||||
 | 
						l.mx.Lock()
 | 
				
			||||||
 | 
						for _, c := range l.cns {
 | 
				
			||||||
 | 
							if err := c.Close(); err != nil {
 | 
				
			||||||
 | 
								retErr = err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						l.cns = nil
 | 
				
			||||||
 | 
						atomic.StoreInt32(&l.len, 0)
 | 
				
			||||||
 | 
						l.mx.Unlock()
 | 
				
			||||||
 | 
						return retErr
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l *connList) closed() bool {
 | 
				
			||||||
 | 
						return l.cns == nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										284
									
								
								internal/pool/pool.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										284
									
								
								internal/pool/pool.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,284 @@
 | 
				
			|||||||
 | 
					package pool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
						"sync/atomic"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gopkg.in/bsm/ratelimit.v1"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var Logger *log.Logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						errClosed      = errors.New("redis: client is closed")
 | 
				
			||||||
 | 
						ErrPoolTimeout = errors.New("redis: connection pool timeout")
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PoolStats contains pool state information and accumulated stats.
 | 
				
			||||||
 | 
					type PoolStats struct {
 | 
				
			||||||
 | 
						Requests uint32 // number of times a connection was requested by the pool
 | 
				
			||||||
 | 
						Hits     uint32 // number of times free connection was found in the pool
 | 
				
			||||||
 | 
						Waits    uint32 // number of times the pool had to wait for a connection
 | 
				
			||||||
 | 
						Timeouts uint32 // number of times a wait timeout occurred
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TotalConns uint32 // the number of total connections in the pool
 | 
				
			||||||
 | 
						FreeConns  uint32 // the number of free connections in the pool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Pooler interface {
 | 
				
			||||||
 | 
						First() *Conn
 | 
				
			||||||
 | 
						Get() (*Conn, bool, error)
 | 
				
			||||||
 | 
						Put(*Conn) error
 | 
				
			||||||
 | 
						Remove(*Conn, error) error
 | 
				
			||||||
 | 
						Len() int
 | 
				
			||||||
 | 
						FreeLen() int
 | 
				
			||||||
 | 
						Close() error
 | 
				
			||||||
 | 
						Stats() *PoolStats
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type dialer func() (*Conn, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ConnPool struct {
 | 
				
			||||||
 | 
						dial dialer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						poolTimeout time.Duration
 | 
				
			||||||
 | 
						idleTimeout time.Duration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rl        *ratelimit.RateLimiter
 | 
				
			||||||
 | 
						conns     *connList
 | 
				
			||||||
 | 
						freeConns chan *Conn
 | 
				
			||||||
 | 
						stats     PoolStats
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_closed int32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lastErr atomic.Value
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewConnPool(dial dialer, poolSize int, poolTimeout, idleTimeout time.Duration) *ConnPool {
 | 
				
			||||||
 | 
						p := &ConnPool{
 | 
				
			||||||
 | 
							dial: dial,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							poolTimeout: poolTimeout,
 | 
				
			||||||
 | 
							idleTimeout: idleTimeout,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							rl:        ratelimit.New(3*poolSize, time.Second),
 | 
				
			||||||
 | 
							conns:     newConnList(poolSize),
 | 
				
			||||||
 | 
							freeConns: make(chan *Conn, poolSize),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if idleTimeout > 0 {
 | 
				
			||||||
 | 
							go p.reaper()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return p
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *ConnPool) closed() bool {
 | 
				
			||||||
 | 
						return atomic.LoadInt32(&p._closed) == 1
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *ConnPool) isIdle(cn *Conn) bool {
 | 
				
			||||||
 | 
						return p.idleTimeout > 0 && time.Since(cn.UsedAt) > p.idleTimeout
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// First returns first non-idle connection from the pool or nil if
 | 
				
			||||||
 | 
					// there are no connections.
 | 
				
			||||||
 | 
					func (p *ConnPool) First() *Conn {
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							select {
 | 
				
			||||||
 | 
							case cn := <-p.freeConns:
 | 
				
			||||||
 | 
								if p.isIdle(cn) {
 | 
				
			||||||
 | 
									var err error
 | 
				
			||||||
 | 
									cn, err = p.replace(cn)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										Logger.Printf("pool.replace failed: %s", err)
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return cn
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						panic("not reached")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// wait waits for free non-idle connection. It returns nil on timeout.
 | 
				
			||||||
 | 
					func (p *ConnPool) wait() *Conn {
 | 
				
			||||||
 | 
						deadline := time.After(p.poolTimeout)
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							select {
 | 
				
			||||||
 | 
							case cn := <-p.freeConns:
 | 
				
			||||||
 | 
								if p.isIdle(cn) {
 | 
				
			||||||
 | 
									var err error
 | 
				
			||||||
 | 
									cn, err = p.replace(cn)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										Logger.Printf("pool.replace failed: %s", err)
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return cn
 | 
				
			||||||
 | 
							case <-deadline:
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						panic("not reached")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Establish a new connection
 | 
				
			||||||
 | 
					func (p *ConnPool) new() (*Conn, error) {
 | 
				
			||||||
 | 
						if p.rl.Limit() {
 | 
				
			||||||
 | 
							err := fmt.Errorf(
 | 
				
			||||||
 | 
								"redis: you open connections too fast (last_error=%q)",
 | 
				
			||||||
 | 
								p.loadLastErr(),
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cn, err := p.dial()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							p.storeLastErr(err.Error())
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return cn, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Get returns existed connection from the pool or creates a new one.
 | 
				
			||||||
 | 
					func (p *ConnPool) Get() (cn *Conn, isNew bool, err error) {
 | 
				
			||||||
 | 
						if p.closed() {
 | 
				
			||||||
 | 
							err = errClosed
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						atomic.AddUint32(&p.stats.Requests, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Fetch first non-idle connection, if available.
 | 
				
			||||||
 | 
						if cn = p.First(); cn != nil {
 | 
				
			||||||
 | 
							atomic.AddUint32(&p.stats.Hits, 1)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Try to create a new one.
 | 
				
			||||||
 | 
						if p.conns.Reserve() {
 | 
				
			||||||
 | 
							isNew = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							cn, err = p.new()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								p.conns.Remove(nil)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							p.conns.Add(cn)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Otherwise, wait for the available connection.
 | 
				
			||||||
 | 
						atomic.AddUint32(&p.stats.Waits, 1)
 | 
				
			||||||
 | 
						if cn = p.wait(); cn != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						atomic.AddUint32(&p.stats.Timeouts, 1)
 | 
				
			||||||
 | 
						err = ErrPoolTimeout
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *ConnPool) Put(cn *Conn) error {
 | 
				
			||||||
 | 
						if cn.Rd.Buffered() != 0 {
 | 
				
			||||||
 | 
							b, _ := cn.Rd.Peek(cn.Rd.Buffered())
 | 
				
			||||||
 | 
							err := fmt.Errorf("connection has unread data: %q", b)
 | 
				
			||||||
 | 
							Logger.Print(err)
 | 
				
			||||||
 | 
							return p.Remove(cn, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						p.freeConns <- cn
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *ConnPool) replace(cn *Conn) (*Conn, error) {
 | 
				
			||||||
 | 
						newcn, err := p.new()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							_ = p.conns.Remove(cn)
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_ = p.conns.Replace(cn, newcn)
 | 
				
			||||||
 | 
						return newcn, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *ConnPool) Remove(cn *Conn, reason error) error {
 | 
				
			||||||
 | 
						p.storeLastErr(reason.Error())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Replace existing connection with new one and unblock waiter.
 | 
				
			||||||
 | 
						newcn, err := p.replace(cn)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						p.freeConns <- newcn
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Len returns total number of connections.
 | 
				
			||||||
 | 
					func (p *ConnPool) Len() int {
 | 
				
			||||||
 | 
						return p.conns.Len()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FreeLen returns number of free connections.
 | 
				
			||||||
 | 
					func (p *ConnPool) FreeLen() int {
 | 
				
			||||||
 | 
						return len(p.freeConns)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *ConnPool) Stats() *PoolStats {
 | 
				
			||||||
 | 
						stats := p.stats
 | 
				
			||||||
 | 
						stats.Requests = atomic.LoadUint32(&p.stats.Requests)
 | 
				
			||||||
 | 
						stats.Waits = atomic.LoadUint32(&p.stats.Waits)
 | 
				
			||||||
 | 
						stats.Timeouts = atomic.LoadUint32(&p.stats.Timeouts)
 | 
				
			||||||
 | 
						stats.TotalConns = uint32(p.Len())
 | 
				
			||||||
 | 
						stats.FreeConns = uint32(p.FreeLen())
 | 
				
			||||||
 | 
						return &stats
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *ConnPool) Close() (retErr error) {
 | 
				
			||||||
 | 
						if !atomic.CompareAndSwapInt32(&p._closed, 0, 1) {
 | 
				
			||||||
 | 
							return errClosed
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Wait for app to free connections, but don't close them immediately.
 | 
				
			||||||
 | 
						for i := 0; i < p.Len(); i++ {
 | 
				
			||||||
 | 
							if cn := p.wait(); cn == nil {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Close all connections.
 | 
				
			||||||
 | 
						if err := p.conns.Close(); err != nil {
 | 
				
			||||||
 | 
							retErr = err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return retErr
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *ConnPool) reaper() {
 | 
				
			||||||
 | 
						ticker := time.NewTicker(time.Minute)
 | 
				
			||||||
 | 
						defer ticker.Stop()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _ = range ticker.C {
 | 
				
			||||||
 | 
							if p.closed() {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// pool.First removes idle connections from the pool and
 | 
				
			||||||
 | 
							// returns first non-idle connection. So just put returned
 | 
				
			||||||
 | 
							// connection back.
 | 
				
			||||||
 | 
							if cn := p.First(); cn != nil {
 | 
				
			||||||
 | 
								p.Put(cn)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *ConnPool) storeLastErr(err string) {
 | 
				
			||||||
 | 
						p.lastErr.Store(err)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *ConnPool) loadLastErr() string {
 | 
				
			||||||
 | 
						if v := p.lastErr.Load(); v != nil {
 | 
				
			||||||
 | 
							return v.(string)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										47
									
								
								internal/pool/pool_single.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								internal/pool/pool_single.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					package pool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SingleConnPool struct {
 | 
				
			||||||
 | 
						cn *Conn
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewSingleConnPool(cn *Conn) *SingleConnPool {
 | 
				
			||||||
 | 
						return &SingleConnPool{
 | 
				
			||||||
 | 
							cn: cn,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *SingleConnPool) First() *Conn {
 | 
				
			||||||
 | 
						return p.cn
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *SingleConnPool) Get() (*Conn, bool, error) {
 | 
				
			||||||
 | 
						return p.cn, false, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *SingleConnPool) Put(cn *Conn) error {
 | 
				
			||||||
 | 
						if p.cn != cn {
 | 
				
			||||||
 | 
							panic("p.cn != cn")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *SingleConnPool) Remove(cn *Conn, _ error) error {
 | 
				
			||||||
 | 
						if p.cn != cn {
 | 
				
			||||||
 | 
							panic("p.cn != cn")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *SingleConnPool) Len() int {
 | 
				
			||||||
 | 
						return 1
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *SingleConnPool) FreeLen() int {
 | 
				
			||||||
 | 
						return 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *SingleConnPool) Stats() *PoolStats { return nil }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *SingleConnPool) Close() error {
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										128
									
								
								internal/pool/pool_sticky.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								internal/pool/pool_sticky.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,128 @@
 | 
				
			|||||||
 | 
					package pool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type StickyConnPool struct {
 | 
				
			||||||
 | 
						pool     *ConnPool
 | 
				
			||||||
 | 
						reusable bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cn     *Conn
 | 
				
			||||||
 | 
						closed bool
 | 
				
			||||||
 | 
						mx     sync.Mutex
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewStickyConnPool(pool *ConnPool, reusable bool) *StickyConnPool {
 | 
				
			||||||
 | 
						return &StickyConnPool{
 | 
				
			||||||
 | 
							pool:     pool,
 | 
				
			||||||
 | 
							reusable: reusable,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *StickyConnPool) First() *Conn {
 | 
				
			||||||
 | 
						p.mx.Lock()
 | 
				
			||||||
 | 
						cn := p.cn
 | 
				
			||||||
 | 
						p.mx.Unlock()
 | 
				
			||||||
 | 
						return cn
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *StickyConnPool) Get() (cn *Conn, isNew bool, err error) {
 | 
				
			||||||
 | 
						defer p.mx.Unlock()
 | 
				
			||||||
 | 
						p.mx.Lock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if p.closed {
 | 
				
			||||||
 | 
							err = errClosed
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if p.cn != nil {
 | 
				
			||||||
 | 
							cn = p.cn
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cn, isNew, err = p.pool.Get()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						p.cn = cn
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *StickyConnPool) put() (err error) {
 | 
				
			||||||
 | 
						err = p.pool.Put(p.cn)
 | 
				
			||||||
 | 
						p.cn = nil
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *StickyConnPool) Put(cn *Conn) error {
 | 
				
			||||||
 | 
						defer p.mx.Unlock()
 | 
				
			||||||
 | 
						p.mx.Lock()
 | 
				
			||||||
 | 
						if p.closed {
 | 
				
			||||||
 | 
							return errClosed
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if p.cn != cn {
 | 
				
			||||||
 | 
							panic("p.cn != cn")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *StickyConnPool) remove(reason error) error {
 | 
				
			||||||
 | 
						err := p.pool.Remove(p.cn, reason)
 | 
				
			||||||
 | 
						p.cn = nil
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *StickyConnPool) Remove(cn *Conn, reason error) error {
 | 
				
			||||||
 | 
						defer p.mx.Unlock()
 | 
				
			||||||
 | 
						p.mx.Lock()
 | 
				
			||||||
 | 
						if p.closed {
 | 
				
			||||||
 | 
							return errClosed
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if p.cn == nil {
 | 
				
			||||||
 | 
							panic("p.cn == nil")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if cn != nil && p.cn != cn {
 | 
				
			||||||
 | 
							panic("p.cn != cn")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return p.remove(reason)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *StickyConnPool) Len() int {
 | 
				
			||||||
 | 
						defer p.mx.Unlock()
 | 
				
			||||||
 | 
						p.mx.Lock()
 | 
				
			||||||
 | 
						if p.cn == nil {
 | 
				
			||||||
 | 
							return 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 1
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *StickyConnPool) FreeLen() int {
 | 
				
			||||||
 | 
						defer p.mx.Unlock()
 | 
				
			||||||
 | 
						p.mx.Lock()
 | 
				
			||||||
 | 
						if p.cn == nil {
 | 
				
			||||||
 | 
							return 1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *StickyConnPool) Stats() *PoolStats { return nil }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *StickyConnPool) Close() error {
 | 
				
			||||||
 | 
						defer p.mx.Unlock()
 | 
				
			||||||
 | 
						p.mx.Lock()
 | 
				
			||||||
 | 
						if p.closed {
 | 
				
			||||||
 | 
							return errClosed
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						p.closed = true
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						if p.cn != nil {
 | 
				
			||||||
 | 
							if p.reusable {
 | 
				
			||||||
 | 
								err = p.put()
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								reason := errors.New("redis: sticky not reusable connection")
 | 
				
			||||||
 | 
								err = p.remove(reason)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										8
									
								
								multi.go
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								multi.go
									
									
									
									
									
								
							@@ -3,6 +3,8 @@ package redis
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gopkg.in/redis.v3/internal/pool"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var errDiscard = errors.New("redis: Discard can be used only inside Exec")
 | 
					var errDiscard = errors.New("redis: Discard can be used only inside Exec")
 | 
				
			||||||
@@ -38,7 +40,7 @@ func (c *Client) Multi() *Multi {
 | 
				
			|||||||
	multi := &Multi{
 | 
						multi := &Multi{
 | 
				
			||||||
		base: &baseClient{
 | 
							base: &baseClient{
 | 
				
			||||||
			opt:      c.opt,
 | 
								opt:      c.opt,
 | 
				
			||||||
			connPool: newStickyConnPool(c.connPool, true),
 | 
								connPool: pool.NewStickyConnPool(c.connPool.(*pool.ConnPool), true),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	multi.commandable.process = multi.process
 | 
						multi.commandable.process = multi.process
 | 
				
			||||||
@@ -137,8 +139,8 @@ func (c *Multi) Exec(f func() error) ([]Cmder, error) {
 | 
				
			|||||||
	return retCmds, err
 | 
						return retCmds, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Multi) execCmds(cn *conn, cmds []Cmder) error {
 | 
					func (c *Multi) execCmds(cn *pool.Conn, cmds []Cmder) error {
 | 
				
			||||||
	err := cn.writeCmds(cmds...)
 | 
						err := writeCmd(cn, cmds...)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		setCmdsErr(cmds[1:len(cmds)-1], err)
 | 
							setCmdsErr(cmds[1:len(cmds)-1], err)
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -145,7 +145,7 @@ var _ = Describe("Multi", func() {
 | 
				
			|||||||
		cn, _, err := client.Pool().Get()
 | 
							cn, _, err := client.Pool().Get()
 | 
				
			||||||
		Expect(err).NotTo(HaveOccurred())
 | 
							Expect(err).NotTo(HaveOccurred())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		cn.SetNetConn(&badConn{})
 | 
							cn.NetConn = &badConn{}
 | 
				
			||||||
		err = client.Pool().Put(cn)
 | 
							err = client.Pool().Put(cn)
 | 
				
			||||||
		Expect(err).NotTo(HaveOccurred())
 | 
							Expect(err).NotTo(HaveOccurred())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -172,7 +172,7 @@ var _ = Describe("Multi", func() {
 | 
				
			|||||||
		cn, _, err := client.Pool().Get()
 | 
							cn, _, err := client.Pool().Get()
 | 
				
			||||||
		Expect(err).NotTo(HaveOccurred())
 | 
							Expect(err).NotTo(HaveOccurred())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		cn.SetNetConn(&badConn{})
 | 
							cn.NetConn = &badConn{}
 | 
				
			||||||
		err = client.Pool().Put(cn)
 | 
							err = client.Pool().Put(cn)
 | 
				
			||||||
		Expect(err).NotTo(HaveOccurred())
 | 
							Expect(err).NotTo(HaveOccurred())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										144
									
								
								options.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								options.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,144 @@
 | 
				
			|||||||
 | 
					package redis
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gopkg.in/redis.v3/internal/pool"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Options struct {
 | 
				
			||||||
 | 
						// The network type, either tcp or unix.
 | 
				
			||||||
 | 
						// Default is tcp.
 | 
				
			||||||
 | 
						Network string
 | 
				
			||||||
 | 
						// host:port address.
 | 
				
			||||||
 | 
						Addr string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Dialer creates new network connection and has priority over
 | 
				
			||||||
 | 
						// Network and Addr options.
 | 
				
			||||||
 | 
						Dialer func() (net.Conn, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// An optional password. Must match the password specified in the
 | 
				
			||||||
 | 
						// requirepass server configuration option.
 | 
				
			||||||
 | 
						Password string
 | 
				
			||||||
 | 
						// A database to be selected after connecting to server.
 | 
				
			||||||
 | 
						DB int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The maximum number of retries before giving up.
 | 
				
			||||||
 | 
						// Default is to not retry failed commands.
 | 
				
			||||||
 | 
						MaxRetries int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Sets the deadline for establishing new connections. If reached,
 | 
				
			||||||
 | 
						// dial will fail with a timeout.
 | 
				
			||||||
 | 
						DialTimeout time.Duration
 | 
				
			||||||
 | 
						// Sets the deadline for socket reads. If reached, commands will
 | 
				
			||||||
 | 
						// fail with a timeout instead of blocking.
 | 
				
			||||||
 | 
						ReadTimeout time.Duration
 | 
				
			||||||
 | 
						// Sets the deadline for socket writes. If reached, commands will
 | 
				
			||||||
 | 
						// fail with a timeout instead of blocking.
 | 
				
			||||||
 | 
						WriteTimeout time.Duration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The maximum number of socket connections.
 | 
				
			||||||
 | 
						// Default is 10 connections.
 | 
				
			||||||
 | 
						PoolSize int
 | 
				
			||||||
 | 
						// Specifies amount of time client waits for connection if all
 | 
				
			||||||
 | 
						// connections are busy before returning an error.
 | 
				
			||||||
 | 
						// Default is 1 seconds.
 | 
				
			||||||
 | 
						PoolTimeout time.Duration
 | 
				
			||||||
 | 
						// Specifies amount of time after which client closes idle
 | 
				
			||||||
 | 
						// connections. Should be less than server's timeout.
 | 
				
			||||||
 | 
						// Default is to not close idle connections.
 | 
				
			||||||
 | 
						IdleTimeout time.Duration
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (opt *Options) getNetwork() string {
 | 
				
			||||||
 | 
						if opt.Network == "" {
 | 
				
			||||||
 | 
							return "tcp"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return opt.Network
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (opt *Options) getDialer() func() (net.Conn, error) {
 | 
				
			||||||
 | 
						if opt.Dialer == nil {
 | 
				
			||||||
 | 
							opt.Dialer = func() (net.Conn, error) {
 | 
				
			||||||
 | 
								return net.DialTimeout(opt.getNetwork(), opt.Addr, opt.getDialTimeout())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return opt.Dialer
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (opt *Options) getPoolDialer() func() (*pool.Conn, error) {
 | 
				
			||||||
 | 
						dial := opt.getDialer()
 | 
				
			||||||
 | 
						return func() (*pool.Conn, error) {
 | 
				
			||||||
 | 
							netcn, err := dial()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							cn := pool.NewConn(netcn)
 | 
				
			||||||
 | 
							return cn, opt.initConn(cn)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (opt *Options) getPoolSize() int {
 | 
				
			||||||
 | 
						if opt.PoolSize == 0 {
 | 
				
			||||||
 | 
							return 10
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return opt.PoolSize
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (opt *Options) getDialTimeout() time.Duration {
 | 
				
			||||||
 | 
						if opt.DialTimeout == 0 {
 | 
				
			||||||
 | 
							return 5 * time.Second
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return opt.DialTimeout
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (opt *Options) getPoolTimeout() time.Duration {
 | 
				
			||||||
 | 
						if opt.PoolTimeout == 0 {
 | 
				
			||||||
 | 
							return 1 * time.Second
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return opt.PoolTimeout
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (opt *Options) getIdleTimeout() time.Duration {
 | 
				
			||||||
 | 
						return opt.IdleTimeout
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (opt *Options) initConn(cn *pool.Conn) error {
 | 
				
			||||||
 | 
						if opt.Password == "" && opt.DB == 0 {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Temp client for Auth and Select.
 | 
				
			||||||
 | 
						client := newClient(opt, pool.NewSingleConnPool(cn))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opt.Password != "" {
 | 
				
			||||||
 | 
							if err := client.Auth(opt.Password).Err(); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opt.DB > 0 {
 | 
				
			||||||
 | 
							if err := client.Select(opt.DB).Err(); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newConnPool(opt *Options) *pool.ConnPool {
 | 
				
			||||||
 | 
						return pool.NewConnPool(
 | 
				
			||||||
 | 
							opt.getPoolDialer(), opt.getPoolSize(), opt.getPoolTimeout(), opt.getIdleTimeout())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PoolStats contains pool state information and accumulated stats.
 | 
				
			||||||
 | 
					type PoolStats struct {
 | 
				
			||||||
 | 
						Requests uint32 // number of times a connection was requested by the pool
 | 
				
			||||||
 | 
						Hits     uint32 // number of times free connection was found in the pool
 | 
				
			||||||
 | 
						Waits    uint32 // number of times the pool had to wait for a connection
 | 
				
			||||||
 | 
						Timeouts uint32 // number of times a wait timeout occurred
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TotalConns uint32 // the number of total connections in the pool
 | 
				
			||||||
 | 
						FreeConns  uint32 // the number of free connections in the pool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										71
									
								
								parser.go
									
									
									
									
									
								
							
							
						
						
									
										71
									
								
								parser.go
									
									
									
									
									
								
							@@ -6,6 +6,8 @@ import (
 | 
				
			|||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gopkg.in/redis.v3/internal/pool"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
@@ -16,7 +18,7 @@ const (
 | 
				
			|||||||
	arrayReply  = '*'
 | 
						arrayReply  = '*'
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type multiBulkParser func(cn *conn, n int64) (interface{}, error)
 | 
					type multiBulkParser func(cn *pool.Conn, n int64) (interface{}, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	errReaderTooSmall = errors.New("redis: reader is too small")
 | 
						errReaderTooSmall = errors.New("redis: reader is too small")
 | 
				
			||||||
@@ -223,8 +225,8 @@ func scan(b []byte, val interface{}) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
//------------------------------------------------------------------------------
 | 
					//------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func readLine(cn *conn) ([]byte, error) {
 | 
					func readLine(cn *pool.Conn) ([]byte, error) {
 | 
				
			||||||
	line, isPrefix, err := cn.rd.ReadLine()
 | 
						line, isPrefix, err := cn.Rd.ReadLine()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return line, err
 | 
							return line, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -243,28 +245,27 @@ func isNilReply(b []byte) bool {
 | 
				
			|||||||
		b[1] == '-' && b[2] == '1'
 | 
							b[1] == '-' && b[2] == '1'
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func readN(cn *conn, n int) ([]byte, error) {
 | 
					func readN(cn *pool.Conn, n int) ([]byte, error) {
 | 
				
			||||||
	var b []byte
 | 
						if d := n - cap(cn.Buf); d > 0 {
 | 
				
			||||||
	if cap(cn.buf) < n {
 | 
							cn.Buf = append(cn.Buf, make([]byte, d)...)
 | 
				
			||||||
		b = make([]byte, n)
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		b = cn.buf[:n]
 | 
							cn.Buf = cn.Buf[:n]
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	_, err := io.ReadFull(cn.rd, b)
 | 
						_, err := io.ReadFull(cn.Rd, cn.Buf)
 | 
				
			||||||
	return b, err
 | 
						return cn.Buf, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//------------------------------------------------------------------------------
 | 
					//------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parseErrorReply(cn *conn, line []byte) error {
 | 
					func parseErrorReply(cn *pool.Conn, line []byte) error {
 | 
				
			||||||
	return errorf(string(line[1:]))
 | 
						return errorf(string(line[1:]))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parseStatusReply(cn *conn, line []byte) ([]byte, error) {
 | 
					func parseStatusReply(cn *pool.Conn, line []byte) ([]byte, error) {
 | 
				
			||||||
	return line[1:], nil
 | 
						return line[1:], nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parseIntReply(cn *conn, line []byte) (int64, error) {
 | 
					func parseIntReply(cn *pool.Conn, line []byte) (int64, error) {
 | 
				
			||||||
	n, err := strconv.ParseInt(bytesToString(line[1:]), 10, 64)
 | 
						n, err := strconv.ParseInt(bytesToString(line[1:]), 10, 64)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return 0, err
 | 
							return 0, err
 | 
				
			||||||
@@ -272,7 +273,7 @@ func parseIntReply(cn *conn, line []byte) (int64, error) {
 | 
				
			|||||||
	return n, nil
 | 
						return n, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func readIntReply(cn *conn) (int64, error) {
 | 
					func readIntReply(cn *pool.Conn) (int64, error) {
 | 
				
			||||||
	line, err := readLine(cn)
 | 
						line, err := readLine(cn)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return 0, err
 | 
							return 0, err
 | 
				
			||||||
@@ -287,7 +288,7 @@ func readIntReply(cn *conn) (int64, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parseBytesReply(cn *conn, line []byte) ([]byte, error) {
 | 
					func parseBytesReply(cn *pool.Conn, line []byte) ([]byte, error) {
 | 
				
			||||||
	if isNilReply(line) {
 | 
						if isNilReply(line) {
 | 
				
			||||||
		return nil, Nil
 | 
							return nil, Nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -305,7 +306,7 @@ func parseBytesReply(cn *conn, line []byte) ([]byte, error) {
 | 
				
			|||||||
	return b[:replyLen], nil
 | 
						return b[:replyLen], nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func readBytesReply(cn *conn) ([]byte, error) {
 | 
					func readBytesReply(cn *pool.Conn) ([]byte, error) {
 | 
				
			||||||
	line, err := readLine(cn)
 | 
						line, err := readLine(cn)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
@@ -322,7 +323,7 @@ func readBytesReply(cn *conn) ([]byte, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func readStringReply(cn *conn) (string, error) {
 | 
					func readStringReply(cn *pool.Conn) (string, error) {
 | 
				
			||||||
	b, err := readBytesReply(cn)
 | 
						b, err := readBytesReply(cn)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return "", err
 | 
							return "", err
 | 
				
			||||||
@@ -330,7 +331,7 @@ func readStringReply(cn *conn) (string, error) {
 | 
				
			|||||||
	return string(b), nil
 | 
						return string(b), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func readFloatReply(cn *conn) (float64, error) {
 | 
					func readFloatReply(cn *pool.Conn) (float64, error) {
 | 
				
			||||||
	b, err := readBytesReply(cn)
 | 
						b, err := readBytesReply(cn)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return 0, err
 | 
							return 0, err
 | 
				
			||||||
@@ -338,7 +339,7 @@ func readFloatReply(cn *conn) (float64, error) {
 | 
				
			|||||||
	return strconv.ParseFloat(bytesToString(b), 64)
 | 
						return strconv.ParseFloat(bytesToString(b), 64)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parseArrayHeader(cn *conn, line []byte) (int64, error) {
 | 
					func parseArrayHeader(cn *pool.Conn, line []byte) (int64, error) {
 | 
				
			||||||
	if isNilReply(line) {
 | 
						if isNilReply(line) {
 | 
				
			||||||
		return 0, Nil
 | 
							return 0, Nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -350,7 +351,7 @@ func parseArrayHeader(cn *conn, line []byte) (int64, error) {
 | 
				
			|||||||
	return n, nil
 | 
						return n, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parseArrayReply(cn *conn, p multiBulkParser, line []byte) (interface{}, error) {
 | 
					func parseArrayReply(cn *pool.Conn, p multiBulkParser, line []byte) (interface{}, error) {
 | 
				
			||||||
	n, err := parseArrayHeader(cn, line)
 | 
						n, err := parseArrayHeader(cn, line)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
@@ -358,7 +359,7 @@ func parseArrayReply(cn *conn, p multiBulkParser, line []byte) (interface{}, err
 | 
				
			|||||||
	return p(cn, n)
 | 
						return p(cn, n)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func readArrayHeader(cn *conn) (int64, error) {
 | 
					func readArrayHeader(cn *pool.Conn) (int64, error) {
 | 
				
			||||||
	line, err := readLine(cn)
 | 
						line, err := readLine(cn)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return 0, err
 | 
							return 0, err
 | 
				
			||||||
@@ -373,7 +374,7 @@ func readArrayHeader(cn *conn) (int64, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func readArrayReply(cn *conn, p multiBulkParser) (interface{}, error) {
 | 
					func readArrayReply(cn *pool.Conn, p multiBulkParser) (interface{}, error) {
 | 
				
			||||||
	line, err := readLine(cn)
 | 
						line, err := readLine(cn)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
@@ -388,7 +389,7 @@ func readArrayReply(cn *conn, p multiBulkParser) (interface{}, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func readReply(cn *conn, p multiBulkParser) (interface{}, error) {
 | 
					func readReply(cn *pool.Conn, p multiBulkParser) (interface{}, error) {
 | 
				
			||||||
	line, err := readLine(cn)
 | 
						line, err := readLine(cn)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
@@ -409,7 +410,7 @@ func readReply(cn *conn, p multiBulkParser) (interface{}, error) {
 | 
				
			|||||||
	return nil, fmt.Errorf("redis: can't parse %.100q", line)
 | 
						return nil, fmt.Errorf("redis: can't parse %.100q", line)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func readScanReply(cn *conn) ([]string, int64, error) {
 | 
					func readScanReply(cn *pool.Conn) ([]string, int64, error) {
 | 
				
			||||||
	n, err := readArrayHeader(cn)
 | 
						n, err := readArrayHeader(cn)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, 0, err
 | 
							return nil, 0, err
 | 
				
			||||||
@@ -445,7 +446,7 @@ func readScanReply(cn *conn) ([]string, int64, error) {
 | 
				
			|||||||
	return keys, cursor, err
 | 
						return keys, cursor, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func sliceParser(cn *conn, n int64) (interface{}, error) {
 | 
					func sliceParser(cn *pool.Conn, n int64) (interface{}, error) {
 | 
				
			||||||
	vals := make([]interface{}, 0, n)
 | 
						vals := make([]interface{}, 0, n)
 | 
				
			||||||
	for i := int64(0); i < n; i++ {
 | 
						for i := int64(0); i < n; i++ {
 | 
				
			||||||
		v, err := readReply(cn, sliceParser)
 | 
							v, err := readReply(cn, sliceParser)
 | 
				
			||||||
@@ -465,7 +466,7 @@ func sliceParser(cn *conn, n int64) (interface{}, error) {
 | 
				
			|||||||
	return vals, nil
 | 
						return vals, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func intSliceParser(cn *conn, n int64) (interface{}, error) {
 | 
					func intSliceParser(cn *pool.Conn, n int64) (interface{}, error) {
 | 
				
			||||||
	ints := make([]int64, 0, n)
 | 
						ints := make([]int64, 0, n)
 | 
				
			||||||
	for i := int64(0); i < n; i++ {
 | 
						for i := int64(0); i < n; i++ {
 | 
				
			||||||
		n, err := readIntReply(cn)
 | 
							n, err := readIntReply(cn)
 | 
				
			||||||
@@ -477,7 +478,7 @@ func intSliceParser(cn *conn, n int64) (interface{}, error) {
 | 
				
			|||||||
	return ints, nil
 | 
						return ints, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func boolSliceParser(cn *conn, n int64) (interface{}, error) {
 | 
					func boolSliceParser(cn *pool.Conn, n int64) (interface{}, error) {
 | 
				
			||||||
	bools := make([]bool, 0, n)
 | 
						bools := make([]bool, 0, n)
 | 
				
			||||||
	for i := int64(0); i < n; i++ {
 | 
						for i := int64(0); i < n; i++ {
 | 
				
			||||||
		n, err := readIntReply(cn)
 | 
							n, err := readIntReply(cn)
 | 
				
			||||||
@@ -489,7 +490,7 @@ func boolSliceParser(cn *conn, n int64) (interface{}, error) {
 | 
				
			|||||||
	return bools, nil
 | 
						return bools, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func stringSliceParser(cn *conn, n int64) (interface{}, error) {
 | 
					func stringSliceParser(cn *pool.Conn, n int64) (interface{}, error) {
 | 
				
			||||||
	ss := make([]string, 0, n)
 | 
						ss := make([]string, 0, n)
 | 
				
			||||||
	for i := int64(0); i < n; i++ {
 | 
						for i := int64(0); i < n; i++ {
 | 
				
			||||||
		s, err := readStringReply(cn)
 | 
							s, err := readStringReply(cn)
 | 
				
			||||||
@@ -504,7 +505,7 @@ func stringSliceParser(cn *conn, n int64) (interface{}, error) {
 | 
				
			|||||||
	return ss, nil
 | 
						return ss, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func floatSliceParser(cn *conn, n int64) (interface{}, error) {
 | 
					func floatSliceParser(cn *pool.Conn, n int64) (interface{}, error) {
 | 
				
			||||||
	nn := make([]float64, 0, n)
 | 
						nn := make([]float64, 0, n)
 | 
				
			||||||
	for i := int64(0); i < n; i++ {
 | 
						for i := int64(0); i < n; i++ {
 | 
				
			||||||
		n, err := readFloatReply(cn)
 | 
							n, err := readFloatReply(cn)
 | 
				
			||||||
@@ -516,7 +517,7 @@ func floatSliceParser(cn *conn, n int64) (interface{}, error) {
 | 
				
			|||||||
	return nn, nil
 | 
						return nn, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func stringStringMapParser(cn *conn, n int64) (interface{}, error) {
 | 
					func stringStringMapParser(cn *pool.Conn, n int64) (interface{}, error) {
 | 
				
			||||||
	m := make(map[string]string, n/2)
 | 
						m := make(map[string]string, n/2)
 | 
				
			||||||
	for i := int64(0); i < n; i += 2 {
 | 
						for i := int64(0); i < n; i += 2 {
 | 
				
			||||||
		key, err := readStringReply(cn)
 | 
							key, err := readStringReply(cn)
 | 
				
			||||||
@@ -534,7 +535,7 @@ func stringStringMapParser(cn *conn, n int64) (interface{}, error) {
 | 
				
			|||||||
	return m, nil
 | 
						return m, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func stringIntMapParser(cn *conn, n int64) (interface{}, error) {
 | 
					func stringIntMapParser(cn *pool.Conn, n int64) (interface{}, error) {
 | 
				
			||||||
	m := make(map[string]int64, n/2)
 | 
						m := make(map[string]int64, n/2)
 | 
				
			||||||
	for i := int64(0); i < n; i += 2 {
 | 
						for i := int64(0); i < n; i += 2 {
 | 
				
			||||||
		key, err := readStringReply(cn)
 | 
							key, err := readStringReply(cn)
 | 
				
			||||||
@@ -552,7 +553,7 @@ func stringIntMapParser(cn *conn, n int64) (interface{}, error) {
 | 
				
			|||||||
	return m, nil
 | 
						return m, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func zSliceParser(cn *conn, n int64) (interface{}, error) {
 | 
					func zSliceParser(cn *pool.Conn, n int64) (interface{}, error) {
 | 
				
			||||||
	zz := make([]Z, n/2)
 | 
						zz := make([]Z, n/2)
 | 
				
			||||||
	for i := int64(0); i < n; i += 2 {
 | 
						for i := int64(0); i < n; i += 2 {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
@@ -572,7 +573,7 @@ func zSliceParser(cn *conn, n int64) (interface{}, error) {
 | 
				
			|||||||
	return zz, nil
 | 
						return zz, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func clusterSlotInfoSliceParser(cn *conn, n int64) (interface{}, error) {
 | 
					func clusterSlotInfoSliceParser(cn *pool.Conn, n int64) (interface{}, error) {
 | 
				
			||||||
	infos := make([]ClusterSlotInfo, 0, n)
 | 
						infos := make([]ClusterSlotInfo, 0, n)
 | 
				
			||||||
	for i := int64(0); i < n; i++ {
 | 
						for i := int64(0); i < n; i++ {
 | 
				
			||||||
		n, err := readArrayHeader(cn)
 | 
							n, err := readArrayHeader(cn)
 | 
				
			||||||
@@ -638,7 +639,7 @@ func clusterSlotInfoSliceParser(cn *conn, n int64) (interface{}, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func newGeoLocationParser(q *GeoRadiusQuery) multiBulkParser {
 | 
					func newGeoLocationParser(q *GeoRadiusQuery) multiBulkParser {
 | 
				
			||||||
	return func(cn *conn, n int64) (interface{}, error) {
 | 
						return func(cn *pool.Conn, n int64) (interface{}, error) {
 | 
				
			||||||
		var loc GeoLocation
 | 
							var loc GeoLocation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
@@ -682,7 +683,7 @@ func newGeoLocationParser(q *GeoRadiusQuery) multiBulkParser {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func newGeoLocationSliceParser(q *GeoRadiusQuery) multiBulkParser {
 | 
					func newGeoLocationSliceParser(q *GeoRadiusQuery) multiBulkParser {
 | 
				
			||||||
	return func(cn *conn, n int64) (interface{}, error) {
 | 
						return func(cn *pool.Conn, n int64) (interface{}, error) {
 | 
				
			||||||
		locs := make([]GeoLocation, 0, n)
 | 
							locs := make([]GeoLocation, 0, n)
 | 
				
			||||||
		for i := int64(0); i < n; i++ {
 | 
							for i := int64(0); i < n; i++ {
 | 
				
			||||||
			v, err := readReply(cn, newGeoLocationParser(q))
 | 
								v, err := readReply(cn, newGeoLocationParser(q))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,8 @@ import (
 | 
				
			|||||||
	"bufio"
 | 
						"bufio"
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gopkg.in/redis.v3/internal/pool"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func BenchmarkParseReplyStatus(b *testing.B) {
 | 
					func BenchmarkParseReplyStatus(b *testing.B) {
 | 
				
			||||||
@@ -31,9 +33,9 @@ func benchmarkParseReply(b *testing.B, reply string, p multiBulkParser, wanterr
 | 
				
			|||||||
	for i := 0; i < b.N; i++ {
 | 
						for i := 0; i < b.N; i++ {
 | 
				
			||||||
		buf.WriteString(reply)
 | 
							buf.WriteString(reply)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	cn := &conn{
 | 
						cn := &pool.Conn{
 | 
				
			||||||
		rd:  bufio.NewReader(buf),
 | 
							Rd:  bufio.NewReader(buf),
 | 
				
			||||||
		buf: make([]byte, 0, defaultBufSize),
 | 
							Buf: make([]byte, 4096),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	b.ResetTimer()
 | 
						b.ResetTimer()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,8 @@ package redis
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"sync/atomic"
 | 
						"sync/atomic"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gopkg.in/redis.v3/internal/pool"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Pipeline implements pipelining as described in
 | 
					// Pipeline implements pipelining as described in
 | 
				
			||||||
@@ -110,8 +112,8 @@ func (pipe *Pipeline) Exec() (cmds []Cmder, retErr error) {
 | 
				
			|||||||
	return cmds, retErr
 | 
						return cmds, retErr
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func execCmds(cn *conn, cmds []Cmder) ([]Cmder, error) {
 | 
					func execCmds(cn *pool.Conn, cmds []Cmder) ([]Cmder, error) {
 | 
				
			||||||
	if err := cn.writeCmds(cmds...); err != nil {
 | 
						if err := writeCmd(cn, cmds...); err != nil {
 | 
				
			||||||
		setCmdsErr(cmds, err)
 | 
							setCmdsErr(cmds, err)
 | 
				
			||||||
		return cmds, err
 | 
							return cmds, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										542
									
								
								pool.go
									
									
									
									
									
								
							
							
						
						
									
										542
									
								
								pool.go
									
									
									
									
									
								
							@@ -1,542 +0,0 @@
 | 
				
			|||||||
package redis
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"errors"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"sync"
 | 
					 | 
				
			||||||
	"sync/atomic"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"gopkg.in/bsm/ratelimit.v1"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var (
 | 
					 | 
				
			||||||
	errClosed      = errors.New("redis: client is closed")
 | 
					 | 
				
			||||||
	errPoolTimeout = errors.New("redis: connection pool timeout")
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// PoolStats contains pool state information and accumulated stats.
 | 
					 | 
				
			||||||
type PoolStats struct {
 | 
					 | 
				
			||||||
	Requests uint32 // number of times a connection was requested by the pool
 | 
					 | 
				
			||||||
	Hits     uint32 // number of times free connection was found in the pool
 | 
					 | 
				
			||||||
	Waits    uint32 // number of times the pool had to wait for a connection
 | 
					 | 
				
			||||||
	Timeouts uint32 // number of times a wait timeout occurred
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	TotalConns uint32 // the number of total connections in the pool
 | 
					 | 
				
			||||||
	FreeConns  uint32 // the number of free connections in the pool
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type pool interface {
 | 
					 | 
				
			||||||
	First() *conn
 | 
					 | 
				
			||||||
	Get() (*conn, bool, error)
 | 
					 | 
				
			||||||
	Put(*conn) error
 | 
					 | 
				
			||||||
	Remove(*conn, error) error
 | 
					 | 
				
			||||||
	Len() int
 | 
					 | 
				
			||||||
	FreeLen() int
 | 
					 | 
				
			||||||
	Close() error
 | 
					 | 
				
			||||||
	Stats() *PoolStats
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type connList struct {
 | 
					 | 
				
			||||||
	cns  []*conn
 | 
					 | 
				
			||||||
	mx   sync.Mutex
 | 
					 | 
				
			||||||
	len  int32 // atomic
 | 
					 | 
				
			||||||
	size int32
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func newConnList(size int) *connList {
 | 
					 | 
				
			||||||
	return &connList{
 | 
					 | 
				
			||||||
		cns:  make([]*conn, 0, size),
 | 
					 | 
				
			||||||
		size: int32(size),
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (l *connList) Len() int {
 | 
					 | 
				
			||||||
	return int(atomic.LoadInt32(&l.len))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Reserve reserves place in the list and returns true on success. The
 | 
					 | 
				
			||||||
// caller must add or remove connection if place was reserved.
 | 
					 | 
				
			||||||
func (l *connList) Reserve() bool {
 | 
					 | 
				
			||||||
	len := atomic.AddInt32(&l.len, 1)
 | 
					 | 
				
			||||||
	reserved := len <= l.size
 | 
					 | 
				
			||||||
	if !reserved {
 | 
					 | 
				
			||||||
		atomic.AddInt32(&l.len, -1)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return reserved
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Add adds connection to the list. The caller must reserve place first.
 | 
					 | 
				
			||||||
func (l *connList) Add(cn *conn) {
 | 
					 | 
				
			||||||
	l.mx.Lock()
 | 
					 | 
				
			||||||
	l.cns = append(l.cns, cn)
 | 
					 | 
				
			||||||
	l.mx.Unlock()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Remove closes connection and removes it from the list.
 | 
					 | 
				
			||||||
func (l *connList) Remove(cn *conn) error {
 | 
					 | 
				
			||||||
	defer l.mx.Unlock()
 | 
					 | 
				
			||||||
	l.mx.Lock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if cn == nil {
 | 
					 | 
				
			||||||
		atomic.AddInt32(&l.len, -1)
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i, c := range l.cns {
 | 
					 | 
				
			||||||
		if c == cn {
 | 
					 | 
				
			||||||
			l.cns = append(l.cns[:i], l.cns[i+1:]...)
 | 
					 | 
				
			||||||
			atomic.AddInt32(&l.len, -1)
 | 
					 | 
				
			||||||
			return cn.Close()
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if l.closed() {
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	panic("conn not found in the list")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (l *connList) Replace(cn, newcn *conn) error {
 | 
					 | 
				
			||||||
	defer l.mx.Unlock()
 | 
					 | 
				
			||||||
	l.mx.Lock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i, c := range l.cns {
 | 
					 | 
				
			||||||
		if c == cn {
 | 
					 | 
				
			||||||
			l.cns[i] = newcn
 | 
					 | 
				
			||||||
			return cn.Close()
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if l.closed() {
 | 
					 | 
				
			||||||
		return newcn.Close()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	panic("conn not found in the list")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (l *connList) Close() (retErr error) {
 | 
					 | 
				
			||||||
	l.mx.Lock()
 | 
					 | 
				
			||||||
	for _, c := range l.cns {
 | 
					 | 
				
			||||||
		if err := c.Close(); err != nil {
 | 
					 | 
				
			||||||
			retErr = err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	l.cns = nil
 | 
					 | 
				
			||||||
	atomic.StoreInt32(&l.len, 0)
 | 
					 | 
				
			||||||
	l.mx.Unlock()
 | 
					 | 
				
			||||||
	return retErr
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (l *connList) closed() bool {
 | 
					 | 
				
			||||||
	return l.cns == nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type connPool struct {
 | 
					 | 
				
			||||||
	dialer func() (*conn, error)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rl        *ratelimit.RateLimiter
 | 
					 | 
				
			||||||
	opt       *Options
 | 
					 | 
				
			||||||
	conns     *connList
 | 
					 | 
				
			||||||
	freeConns chan *conn
 | 
					 | 
				
			||||||
	stats     PoolStats
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_closed int32
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	lastErr atomic.Value
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func newConnPool(opt *Options) *connPool {
 | 
					 | 
				
			||||||
	p := &connPool{
 | 
					 | 
				
			||||||
		dialer: newConnDialer(opt),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		rl:        ratelimit.New(3*opt.getPoolSize(), time.Second),
 | 
					 | 
				
			||||||
		opt:       opt,
 | 
					 | 
				
			||||||
		conns:     newConnList(opt.getPoolSize()),
 | 
					 | 
				
			||||||
		freeConns: make(chan *conn, opt.getPoolSize()),
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if p.opt.getIdleTimeout() > 0 {
 | 
					 | 
				
			||||||
		go p.reaper()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return p
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *connPool) closed() bool {
 | 
					 | 
				
			||||||
	return atomic.LoadInt32(&p._closed) == 1
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *connPool) isIdle(cn *conn) bool {
 | 
					 | 
				
			||||||
	return p.opt.getIdleTimeout() > 0 && time.Since(cn.UsedAt) > p.opt.getIdleTimeout()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// First returns first non-idle connection from the pool or nil if
 | 
					 | 
				
			||||||
// there are no connections.
 | 
					 | 
				
			||||||
func (p *connPool) First() *conn {
 | 
					 | 
				
			||||||
	for {
 | 
					 | 
				
			||||||
		select {
 | 
					 | 
				
			||||||
		case cn := <-p.freeConns:
 | 
					 | 
				
			||||||
			if p.isIdle(cn) {
 | 
					 | 
				
			||||||
				var err error
 | 
					 | 
				
			||||||
				cn, err = p.replace(cn)
 | 
					 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					Logger.Printf("pool.replace failed: %s", err)
 | 
					 | 
				
			||||||
					continue
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return cn
 | 
					 | 
				
			||||||
		default:
 | 
					 | 
				
			||||||
			return nil
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	panic("not reached")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// wait waits for free non-idle connection. It returns nil on timeout.
 | 
					 | 
				
			||||||
func (p *connPool) wait() *conn {
 | 
					 | 
				
			||||||
	deadline := time.After(p.opt.getPoolTimeout())
 | 
					 | 
				
			||||||
	for {
 | 
					 | 
				
			||||||
		select {
 | 
					 | 
				
			||||||
		case cn := <-p.freeConns:
 | 
					 | 
				
			||||||
			if p.isIdle(cn) {
 | 
					 | 
				
			||||||
				var err error
 | 
					 | 
				
			||||||
				cn, err = p.replace(cn)
 | 
					 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					Logger.Printf("pool.replace failed: %s", err)
 | 
					 | 
				
			||||||
					continue
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return cn
 | 
					 | 
				
			||||||
		case <-deadline:
 | 
					 | 
				
			||||||
			return nil
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	panic("not reached")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Establish a new connection
 | 
					 | 
				
			||||||
func (p *connPool) new() (*conn, error) {
 | 
					 | 
				
			||||||
	if p.rl.Limit() {
 | 
					 | 
				
			||||||
		err := fmt.Errorf(
 | 
					 | 
				
			||||||
			"redis: you open connections too fast (last_error=%q)",
 | 
					 | 
				
			||||||
			p.loadLastErr(),
 | 
					 | 
				
			||||||
		)
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cn, err := p.dialer()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		p.storeLastErr(err.Error())
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return cn, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Get returns existed connection from the pool or creates a new one.
 | 
					 | 
				
			||||||
func (p *connPool) Get() (cn *conn, isNew bool, err error) {
 | 
					 | 
				
			||||||
	if p.closed() {
 | 
					 | 
				
			||||||
		err = errClosed
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	atomic.AddUint32(&p.stats.Requests, 1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Fetch first non-idle connection, if available.
 | 
					 | 
				
			||||||
	if cn = p.First(); cn != nil {
 | 
					 | 
				
			||||||
		atomic.AddUint32(&p.stats.Hits, 1)
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Try to create a new one.
 | 
					 | 
				
			||||||
	if p.conns.Reserve() {
 | 
					 | 
				
			||||||
		isNew = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		cn, err = p.new()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			p.conns.Remove(nil)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		p.conns.Add(cn)
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Otherwise, wait for the available connection.
 | 
					 | 
				
			||||||
	atomic.AddUint32(&p.stats.Waits, 1)
 | 
					 | 
				
			||||||
	if cn = p.wait(); cn != nil {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	atomic.AddUint32(&p.stats.Timeouts, 1)
 | 
					 | 
				
			||||||
	err = errPoolTimeout
 | 
					 | 
				
			||||||
	return
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *connPool) Put(cn *conn) error {
 | 
					 | 
				
			||||||
	if cn.rd.Buffered() != 0 {
 | 
					 | 
				
			||||||
		b, _ := cn.rd.Peek(cn.rd.Buffered())
 | 
					 | 
				
			||||||
		err := fmt.Errorf("connection has unread data: %q", b)
 | 
					 | 
				
			||||||
		Logger.Print(err)
 | 
					 | 
				
			||||||
		return p.Remove(cn, err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	p.freeConns <- cn
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *connPool) replace(cn *conn) (*conn, error) {
 | 
					 | 
				
			||||||
	newcn, err := p.new()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		_ = p.conns.Remove(cn)
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	_ = p.conns.Replace(cn, newcn)
 | 
					 | 
				
			||||||
	return newcn, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *connPool) Remove(cn *conn, reason error) error {
 | 
					 | 
				
			||||||
	p.storeLastErr(reason.Error())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Replace existing connection with new one and unblock waiter.
 | 
					 | 
				
			||||||
	newcn, err := p.replace(cn)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	p.freeConns <- newcn
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Len returns total number of connections.
 | 
					 | 
				
			||||||
func (p *connPool) Len() int {
 | 
					 | 
				
			||||||
	return p.conns.Len()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// FreeLen returns number of free connections.
 | 
					 | 
				
			||||||
func (p *connPool) FreeLen() int {
 | 
					 | 
				
			||||||
	return len(p.freeConns)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *connPool) Stats() *PoolStats {
 | 
					 | 
				
			||||||
	stats := p.stats
 | 
					 | 
				
			||||||
	stats.Requests = atomic.LoadUint32(&p.stats.Requests)
 | 
					 | 
				
			||||||
	stats.Waits = atomic.LoadUint32(&p.stats.Waits)
 | 
					 | 
				
			||||||
	stats.Timeouts = atomic.LoadUint32(&p.stats.Timeouts)
 | 
					 | 
				
			||||||
	stats.TotalConns = uint32(p.Len())
 | 
					 | 
				
			||||||
	stats.FreeConns = uint32(p.FreeLen())
 | 
					 | 
				
			||||||
	return &stats
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *connPool) Close() (retErr error) {
 | 
					 | 
				
			||||||
	if !atomic.CompareAndSwapInt32(&p._closed, 0, 1) {
 | 
					 | 
				
			||||||
		return errClosed
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// Wait for app to free connections, but don't close them immediately.
 | 
					 | 
				
			||||||
	for i := 0; i < p.Len(); i++ {
 | 
					 | 
				
			||||||
		if cn := p.wait(); cn == nil {
 | 
					 | 
				
			||||||
			break
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// Close all connections.
 | 
					 | 
				
			||||||
	if err := p.conns.Close(); err != nil {
 | 
					 | 
				
			||||||
		retErr = err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return retErr
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *connPool) reaper() {
 | 
					 | 
				
			||||||
	ticker := time.NewTicker(time.Minute)
 | 
					 | 
				
			||||||
	defer ticker.Stop()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _ = range ticker.C {
 | 
					 | 
				
			||||||
		if p.closed() {
 | 
					 | 
				
			||||||
			break
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// pool.First removes idle connections from the pool and
 | 
					 | 
				
			||||||
		// returns first non-idle connection. So just put returned
 | 
					 | 
				
			||||||
		// connection back.
 | 
					 | 
				
			||||||
		if cn := p.First(); cn != nil {
 | 
					 | 
				
			||||||
			p.Put(cn)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *connPool) storeLastErr(err string) {
 | 
					 | 
				
			||||||
	p.lastErr.Store(err)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *connPool) loadLastErr() string {
 | 
					 | 
				
			||||||
	if v := p.lastErr.Load(); v != nil {
 | 
					 | 
				
			||||||
		return v.(string)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return ""
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type singleConnPool struct {
 | 
					 | 
				
			||||||
	cn *conn
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func newSingleConnPool(cn *conn) *singleConnPool {
 | 
					 | 
				
			||||||
	return &singleConnPool{
 | 
					 | 
				
			||||||
		cn: cn,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *singleConnPool) First() *conn {
 | 
					 | 
				
			||||||
	return p.cn
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *singleConnPool) Get() (*conn, bool, error) {
 | 
					 | 
				
			||||||
	return p.cn, false, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *singleConnPool) Put(cn *conn) error {
 | 
					 | 
				
			||||||
	if p.cn != cn {
 | 
					 | 
				
			||||||
		panic("p.cn != cn")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *singleConnPool) Remove(cn *conn, _ error) error {
 | 
					 | 
				
			||||||
	if p.cn != cn {
 | 
					 | 
				
			||||||
		panic("p.cn != cn")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *singleConnPool) Len() int {
 | 
					 | 
				
			||||||
	return 1
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *singleConnPool) FreeLen() int {
 | 
					 | 
				
			||||||
	return 0
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *singleConnPool) Stats() *PoolStats { return nil }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *singleConnPool) Close() error {
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type stickyConnPool struct {
 | 
					 | 
				
			||||||
	pool     pool
 | 
					 | 
				
			||||||
	reusable bool
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cn     *conn
 | 
					 | 
				
			||||||
	closed bool
 | 
					 | 
				
			||||||
	mx     sync.Mutex
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func newStickyConnPool(pool pool, reusable bool) *stickyConnPool {
 | 
					 | 
				
			||||||
	return &stickyConnPool{
 | 
					 | 
				
			||||||
		pool:     pool,
 | 
					 | 
				
			||||||
		reusable: reusable,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *stickyConnPool) First() *conn {
 | 
					 | 
				
			||||||
	p.mx.Lock()
 | 
					 | 
				
			||||||
	cn := p.cn
 | 
					 | 
				
			||||||
	p.mx.Unlock()
 | 
					 | 
				
			||||||
	return cn
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *stickyConnPool) Get() (cn *conn, isNew bool, err error) {
 | 
					 | 
				
			||||||
	defer p.mx.Unlock()
 | 
					 | 
				
			||||||
	p.mx.Lock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if p.closed {
 | 
					 | 
				
			||||||
		err = errClosed
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if p.cn != nil {
 | 
					 | 
				
			||||||
		cn = p.cn
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cn, isNew, err = p.pool.Get()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	p.cn = cn
 | 
					 | 
				
			||||||
	return
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *stickyConnPool) put() (err error) {
 | 
					 | 
				
			||||||
	err = p.pool.Put(p.cn)
 | 
					 | 
				
			||||||
	p.cn = nil
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *stickyConnPool) Put(cn *conn) error {
 | 
					 | 
				
			||||||
	defer p.mx.Unlock()
 | 
					 | 
				
			||||||
	p.mx.Lock()
 | 
					 | 
				
			||||||
	if p.closed {
 | 
					 | 
				
			||||||
		return errClosed
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if p.cn != cn {
 | 
					 | 
				
			||||||
		panic("p.cn != cn")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *stickyConnPool) remove(reason error) error {
 | 
					 | 
				
			||||||
	err := p.pool.Remove(p.cn, reason)
 | 
					 | 
				
			||||||
	p.cn = nil
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *stickyConnPool) Remove(cn *conn, reason error) error {
 | 
					 | 
				
			||||||
	defer p.mx.Unlock()
 | 
					 | 
				
			||||||
	p.mx.Lock()
 | 
					 | 
				
			||||||
	if p.closed {
 | 
					 | 
				
			||||||
		return errClosed
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if p.cn == nil {
 | 
					 | 
				
			||||||
		panic("p.cn == nil")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if cn != nil && p.cn != cn {
 | 
					 | 
				
			||||||
		panic("p.cn != cn")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return p.remove(reason)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *stickyConnPool) Len() int {
 | 
					 | 
				
			||||||
	defer p.mx.Unlock()
 | 
					 | 
				
			||||||
	p.mx.Lock()
 | 
					 | 
				
			||||||
	if p.cn == nil {
 | 
					 | 
				
			||||||
		return 0
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return 1
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *stickyConnPool) FreeLen() int {
 | 
					 | 
				
			||||||
	defer p.mx.Unlock()
 | 
					 | 
				
			||||||
	p.mx.Lock()
 | 
					 | 
				
			||||||
	if p.cn == nil {
 | 
					 | 
				
			||||||
		return 1
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return 0
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *stickyConnPool) Stats() *PoolStats { return nil }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *stickyConnPool) Close() error {
 | 
					 | 
				
			||||||
	defer p.mx.Unlock()
 | 
					 | 
				
			||||||
	p.mx.Lock()
 | 
					 | 
				
			||||||
	if p.closed {
 | 
					 | 
				
			||||||
		return errClosed
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	p.closed = true
 | 
					 | 
				
			||||||
	var err error
 | 
					 | 
				
			||||||
	if p.cn != nil {
 | 
					 | 
				
			||||||
		if p.reusable {
 | 
					 | 
				
			||||||
			err = p.put()
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			reason := errors.New("redis: sticky not reusable connection")
 | 
					 | 
				
			||||||
			err = p.remove(reason)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										17
									
								
								pubsub.go
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								pubsub.go
									
									
									
									
									
								
							@@ -4,6 +4,8 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gopkg.in/redis.v3/internal/pool"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Posts a message to the given channel.
 | 
					// Posts a message to the given channel.
 | 
				
			||||||
@@ -30,7 +32,7 @@ func (c *Client) PubSub() *PubSub {
 | 
				
			|||||||
	return &PubSub{
 | 
						return &PubSub{
 | 
				
			||||||
		base: &baseClient{
 | 
							base: &baseClient{
 | 
				
			||||||
			opt:      c.opt,
 | 
								opt:      c.opt,
 | 
				
			||||||
			connPool: newStickyConnPool(c.connPool, false),
 | 
								connPool: pool.NewStickyConnPool(c.connPool.(*pool.ConnPool), false),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -47,19 +49,20 @@ func (c *Client) PSubscribe(channels ...string) (*PubSub, error) {
 | 
				
			|||||||
	return pubsub, pubsub.PSubscribe(channels...)
 | 
						return pubsub, pubsub.PSubscribe(channels...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *PubSub) subscribe(cmd string, channels ...string) error {
 | 
					func (c *PubSub) subscribe(redisCmd string, channels ...string) error {
 | 
				
			||||||
	cn, _, err := c.base.conn()
 | 
						cn, _, err := c.base.conn()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	args := make([]interface{}, 1+len(channels))
 | 
						args := make([]interface{}, 1+len(channels))
 | 
				
			||||||
	args[0] = cmd
 | 
						args[0] = redisCmd
 | 
				
			||||||
	for i, channel := range channels {
 | 
						for i, channel := range channels {
 | 
				
			||||||
		args[1+i] = channel
 | 
							args[1+i] = channel
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	req := NewSliceCmd(args...)
 | 
						cmd := NewSliceCmd(args...)
 | 
				
			||||||
	return cn.writeCmds(req)
 | 
					
 | 
				
			||||||
 | 
						return writeCmd(cn, cmd)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Subscribes the client to the specified channels.
 | 
					// Subscribes the client to the specified channels.
 | 
				
			||||||
@@ -132,7 +135,7 @@ func (c *PubSub) Ping(payload string) error {
 | 
				
			|||||||
		args = append(args, payload)
 | 
							args = append(args, payload)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	cmd := NewCmd(args...)
 | 
						cmd := NewCmd(args...)
 | 
				
			||||||
	return cn.writeCmds(cmd)
 | 
						return writeCmd(cn, cmd)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Message received after a successful subscription to channel.
 | 
					// Message received after a successful subscription to channel.
 | 
				
			||||||
@@ -296,7 +299,7 @@ func (c *PubSub) ReceiveMessage() (*Message, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *PubSub) putConn(cn *conn, err error) {
 | 
					func (c *PubSub) putConn(cn *pool.Conn, err error) {
 | 
				
			||||||
	if !c.base.putConn(cn, err, true) {
 | 
						if !c.base.putConn(cn, err, true) {
 | 
				
			||||||
		c.nsub = 0
 | 
							c.nsub = 0
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -291,10 +291,10 @@ var _ = Describe("PubSub", func() {
 | 
				
			|||||||
	expectReceiveMessageOnError := func(pubsub *redis.PubSub) {
 | 
						expectReceiveMessageOnError := func(pubsub *redis.PubSub) {
 | 
				
			||||||
		cn1, _, err := pubsub.Pool().Get()
 | 
							cn1, _, err := pubsub.Pool().Get()
 | 
				
			||||||
		Expect(err).NotTo(HaveOccurred())
 | 
							Expect(err).NotTo(HaveOccurred())
 | 
				
			||||||
		cn1.SetNetConn(&badConn{
 | 
							cn1.NetConn = &badConn{
 | 
				
			||||||
			readErr:  io.EOF,
 | 
								readErr:  io.EOF,
 | 
				
			||||||
			writeErr: io.EOF,
 | 
								writeErr: io.EOF,
 | 
				
			||||||
		})
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		done := make(chan bool, 1)
 | 
							done := make(chan bool, 1)
 | 
				
			||||||
		go func() {
 | 
							go func() {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										130
									
								
								redis.go
									
									
									
									
									
								
							
							
						
						
									
										130
									
								
								redis.go
									
									
									
									
									
								
							@@ -3,15 +3,26 @@ package redis // import "gopkg.in/redis.v3"
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
	"net"
 | 
					 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"time"
 | 
						"sync/atomic"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gopkg.in/redis.v3/internal/pool"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var Logger = log.New(os.Stderr, "redis: ", log.LstdFlags)
 | 
					// Deprecated. Use SetLogger instead.
 | 
				
			||||||
 | 
					var Logger *log.Logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						SetLogger(log.New(os.Stderr, "redis: ", log.LstdFlags))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func SetLogger(logger *log.Logger) {
 | 
				
			||||||
 | 
						Logger = logger
 | 
				
			||||||
 | 
						pool.Logger = logger
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type baseClient struct {
 | 
					type baseClient struct {
 | 
				
			||||||
	connPool pool
 | 
						connPool pool.Pooler
 | 
				
			||||||
	opt      *Options
 | 
						opt      *Options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	onClose func() error // hook called when client is closed
 | 
						onClose func() error // hook called when client is closed
 | 
				
			||||||
@@ -21,11 +32,11 @@ func (c *baseClient) String() string {
 | 
				
			|||||||
	return fmt.Sprintf("Redis<%s db:%d>", c.opt.Addr, c.opt.DB)
 | 
						return fmt.Sprintf("Redis<%s db:%d>", c.opt.Addr, c.opt.DB)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *baseClient) conn() (*conn, bool, error) {
 | 
					func (c *baseClient) conn() (*pool.Conn, bool, error) {
 | 
				
			||||||
	return c.connPool.Get()
 | 
						return c.connPool.Get()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *baseClient) putConn(cn *conn, err error, allowTimeout bool) bool {
 | 
					func (c *baseClient) putConn(cn *pool.Conn, err error, allowTimeout bool) bool {
 | 
				
			||||||
	if isBadConn(err, allowTimeout) {
 | 
						if isBadConn(err, allowTimeout) {
 | 
				
			||||||
		err = c.connPool.Remove(cn, err)
 | 
							err = c.connPool.Remove(cn, err)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
@@ -61,7 +72,7 @@ func (c *baseClient) process(cmd Cmder) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		cn.WriteTimeout = c.opt.WriteTimeout
 | 
							cn.WriteTimeout = c.opt.WriteTimeout
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err := cn.writeCmds(cmd); err != nil {
 | 
							if err := writeCmd(cn, cmd); err != nil {
 | 
				
			||||||
			c.putConn(cn, err, false)
 | 
								c.putConn(cn, err, false)
 | 
				
			||||||
			cmd.setErr(err)
 | 
								cmd.setErr(err)
 | 
				
			||||||
			if shouldRetry(err) {
 | 
								if shouldRetry(err) {
 | 
				
			||||||
@@ -99,93 +110,6 @@ func (c *baseClient) Close() error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
//------------------------------------------------------------------------------
 | 
					//------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Options struct {
 | 
					 | 
				
			||||||
	// The network type, either tcp or unix.
 | 
					 | 
				
			||||||
	// Default is tcp.
 | 
					 | 
				
			||||||
	Network string
 | 
					 | 
				
			||||||
	// host:port address.
 | 
					 | 
				
			||||||
	Addr string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Dialer creates new network connection and has priority over
 | 
					 | 
				
			||||||
	// Network and Addr options.
 | 
					 | 
				
			||||||
	Dialer func() (net.Conn, error)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// An optional password. Must match the password specified in the
 | 
					 | 
				
			||||||
	// requirepass server configuration option.
 | 
					 | 
				
			||||||
	Password string
 | 
					 | 
				
			||||||
	// A database to be selected after connecting to server.
 | 
					 | 
				
			||||||
	DB int64
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// The maximum number of retries before giving up.
 | 
					 | 
				
			||||||
	// Default is to not retry failed commands.
 | 
					 | 
				
			||||||
	MaxRetries int
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Sets the deadline for establishing new connections. If reached,
 | 
					 | 
				
			||||||
	// dial will fail with a timeout.
 | 
					 | 
				
			||||||
	DialTimeout time.Duration
 | 
					 | 
				
			||||||
	// Sets the deadline for socket reads. If reached, commands will
 | 
					 | 
				
			||||||
	// fail with a timeout instead of blocking.
 | 
					 | 
				
			||||||
	ReadTimeout time.Duration
 | 
					 | 
				
			||||||
	// Sets the deadline for socket writes. If reached, commands will
 | 
					 | 
				
			||||||
	// fail with a timeout instead of blocking.
 | 
					 | 
				
			||||||
	WriteTimeout time.Duration
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// The maximum number of socket connections.
 | 
					 | 
				
			||||||
	// Default is 10 connections.
 | 
					 | 
				
			||||||
	PoolSize int
 | 
					 | 
				
			||||||
	// Specifies amount of time client waits for connection if all
 | 
					 | 
				
			||||||
	// connections are busy before returning an error.
 | 
					 | 
				
			||||||
	// Default is 1 seconds.
 | 
					 | 
				
			||||||
	PoolTimeout time.Duration
 | 
					 | 
				
			||||||
	// Specifies amount of time after which client closes idle
 | 
					 | 
				
			||||||
	// connections. Should be less than server's timeout.
 | 
					 | 
				
			||||||
	// Default is to not close idle connections.
 | 
					 | 
				
			||||||
	IdleTimeout time.Duration
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (opt *Options) getNetwork() string {
 | 
					 | 
				
			||||||
	if opt.Network == "" {
 | 
					 | 
				
			||||||
		return "tcp"
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return opt.Network
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (opt *Options) getDialer() func() (net.Conn, error) {
 | 
					 | 
				
			||||||
	if opt.Dialer == nil {
 | 
					 | 
				
			||||||
		opt.Dialer = func() (net.Conn, error) {
 | 
					 | 
				
			||||||
			return net.DialTimeout(opt.getNetwork(), opt.Addr, opt.getDialTimeout())
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return opt.Dialer
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (opt *Options) getPoolSize() int {
 | 
					 | 
				
			||||||
	if opt.PoolSize == 0 {
 | 
					 | 
				
			||||||
		return 10
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return opt.PoolSize
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (opt *Options) getDialTimeout() time.Duration {
 | 
					 | 
				
			||||||
	if opt.DialTimeout == 0 {
 | 
					 | 
				
			||||||
		return 5 * time.Second
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return opt.DialTimeout
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (opt *Options) getPoolTimeout() time.Duration {
 | 
					 | 
				
			||||||
	if opt.PoolTimeout == 0 {
 | 
					 | 
				
			||||||
		return 1 * time.Second
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return opt.PoolTimeout
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (opt *Options) getIdleTimeout() time.Duration {
 | 
					 | 
				
			||||||
	return opt.IdleTimeout
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Client is a Redis client representing a pool of zero or more
 | 
					// Client is a Redis client representing a pool of zero or more
 | 
				
			||||||
// underlying connections. It's safe for concurrent use by multiple
 | 
					// underlying connections. It's safe for concurrent use by multiple
 | 
				
			||||||
// goroutines.
 | 
					// goroutines.
 | 
				
			||||||
@@ -194,7 +118,7 @@ type Client struct {
 | 
				
			|||||||
	commandable
 | 
						commandable
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func newClient(opt *Options, pool pool) *Client {
 | 
					func newClient(opt *Options, pool pool.Pooler) *Client {
 | 
				
			||||||
	base := baseClient{opt: opt, connPool: pool}
 | 
						base := baseClient{opt: opt, connPool: pool}
 | 
				
			||||||
	return &Client{
 | 
						return &Client{
 | 
				
			||||||
		baseClient: base,
 | 
							baseClient: base,
 | 
				
			||||||
@@ -206,11 +130,19 @@ func newClient(opt *Options, pool pool) *Client {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// NewClient returns a client to the Redis Server specified by Options.
 | 
					// NewClient returns a client to the Redis Server specified by Options.
 | 
				
			||||||
func NewClient(opt *Options) *Client {
 | 
					func NewClient(opt *Options) *Client {
 | 
				
			||||||
	pool := newConnPool(opt)
 | 
						return newClient(opt, newConnPool(opt))
 | 
				
			||||||
	return newClient(opt, pool)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PoolStats returns connection pool stats
 | 
					// PoolStats returns connection pool stats.
 | 
				
			||||||
func (c *Client) PoolStats() *PoolStats {
 | 
					func (c *Client) PoolStats() *PoolStats {
 | 
				
			||||||
	return c.connPool.Stats()
 | 
						s := c.connPool.Stats()
 | 
				
			||||||
 | 
						return &PoolStats{
 | 
				
			||||||
 | 
							Requests: atomic.LoadUint32(&s.Requests),
 | 
				
			||||||
 | 
							Hits:     atomic.LoadUint32(&s.Hits),
 | 
				
			||||||
 | 
							Waits:    atomic.LoadUint32(&s.Waits),
 | 
				
			||||||
 | 
							Timeouts: atomic.LoadUint32(&s.Timeouts),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							TotalConns: atomic.LoadUint32(&s.TotalConns),
 | 
				
			||||||
 | 
							FreeConns:  atomic.LoadUint32(&s.FreeConns),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -160,7 +160,7 @@ var _ = Describe("Client", func() {
 | 
				
			|||||||
		cn, _, err := client.Pool().Get()
 | 
							cn, _, err := client.Pool().Get()
 | 
				
			||||||
		Expect(err).NotTo(HaveOccurred())
 | 
							Expect(err).NotTo(HaveOccurred())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		cn.SetNetConn(&badConn{})
 | 
							cn.NetConn = &badConn{}
 | 
				
			||||||
		err = client.Pool().Put(cn)
 | 
							err = client.Pool().Put(cn)
 | 
				
			||||||
		Expect(err).NotTo(HaveOccurred())
 | 
							Expect(err).NotTo(HaveOccurred())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -174,10 +174,6 @@ var _ = Describe("Client", func() {
 | 
				
			|||||||
		Expect(cn.UsedAt).NotTo(BeZero())
 | 
							Expect(cn.UsedAt).NotTo(BeZero())
 | 
				
			||||||
		createdAt := cn.UsedAt
 | 
							createdAt := cn.UsedAt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		future := time.Now().Add(time.Hour)
 | 
					 | 
				
			||||||
		redis.SetTime(future)
 | 
					 | 
				
			||||||
		defer redis.RestoreTime()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		err = client.Pool().Put(cn)
 | 
							err = client.Pool().Put(cn)
 | 
				
			||||||
		Expect(err).NotTo(HaveOccurred())
 | 
							Expect(err).NotTo(HaveOccurred())
 | 
				
			||||||
		Expect(cn.UsedAt.Equal(createdAt)).To(BeTrue())
 | 
							Expect(cn.UsedAt.Equal(createdAt)).To(BeTrue())
 | 
				
			||||||
@@ -187,6 +183,6 @@ var _ = Describe("Client", func() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		cn = client.Pool().First()
 | 
							cn = client.Pool().First()
 | 
				
			||||||
		Expect(cn).NotTo(BeNil())
 | 
							Expect(cn).NotTo(BeNil())
 | 
				
			||||||
		Expect(cn.UsedAt.Equal(future)).To(BeTrue())
 | 
							Expect(cn.UsedAt.After(createdAt)).To(BeTrue())
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								ring.go
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								ring.go
									
									
									
									
									
								
							@@ -8,6 +8,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"gopkg.in/redis.v3/internal/consistenthash"
 | 
						"gopkg.in/redis.v3/internal/consistenthash"
 | 
				
			||||||
	"gopkg.in/redis.v3/internal/hashtag"
 | 
						"gopkg.in/redis.v3/internal/hashtag"
 | 
				
			||||||
 | 
						"gopkg.in/redis.v3/internal/pool"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
@@ -200,7 +201,7 @@ func (ring *Ring) heartbeat() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		for _, shard := range ring.shards {
 | 
							for _, shard := range ring.shards {
 | 
				
			||||||
			err := shard.Client.Ping().Err()
 | 
								err := shard.Client.Ping().Err()
 | 
				
			||||||
			if shard.Vote(err == nil || err == errPoolTimeout) {
 | 
								if shard.Vote(err == nil || err == pool.ErrPoolTimeout) {
 | 
				
			||||||
				Logger.Printf("ring shard state changed: %s", shard)
 | 
									Logger.Printf("ring shard state changed: %s", shard)
 | 
				
			||||||
				rebalance = true
 | 
									rebalance = true
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								sentinel.go
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								sentinel.go
									
									
									
									
									
								
							@@ -7,6 +7,8 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gopkg.in/redis.v3/internal/pool"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//------------------------------------------------------------------------------
 | 
					//------------------------------------------------------------------------------
 | 
				
			||||||
@@ -103,7 +105,7 @@ func (c *sentinelClient) PubSub() *PubSub {
 | 
				
			|||||||
	return &PubSub{
 | 
						return &PubSub{
 | 
				
			||||||
		base: &baseClient{
 | 
							base: &baseClient{
 | 
				
			||||||
			opt:      c.opt,
 | 
								opt:      c.opt,
 | 
				
			||||||
			connPool: newStickyConnPool(c.connPool, false),
 | 
								connPool: pool.NewStickyConnPool(c.connPool.(*pool.ConnPool), false),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -126,7 +128,7 @@ type sentinelFailover struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	opt *Options
 | 
						opt *Options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pool     pool
 | 
						pool     *pool.ConnPool
 | 
				
			||||||
	poolOnce sync.Once
 | 
						poolOnce sync.Once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mu       sync.RWMutex
 | 
						mu       sync.RWMutex
 | 
				
			||||||
@@ -145,7 +147,7 @@ func (d *sentinelFailover) dial() (net.Conn, error) {
 | 
				
			|||||||
	return net.DialTimeout("tcp", addr, d.opt.DialTimeout)
 | 
						return net.DialTimeout("tcp", addr, d.opt.DialTimeout)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *sentinelFailover) Pool() pool {
 | 
					func (d *sentinelFailover) Pool() *pool.ConnPool {
 | 
				
			||||||
	d.poolOnce.Do(func() {
 | 
						d.poolOnce.Do(func() {
 | 
				
			||||||
		d.opt.Dialer = d.dial
 | 
							d.opt.Dialer = d.dial
 | 
				
			||||||
		d.pool = newConnPool(d.opt)
 | 
							d.pool = newConnPool(d.opt)
 | 
				
			||||||
@@ -252,7 +254,7 @@ func (d *sentinelFailover) closeOldConns(newMaster string) {
 | 
				
			|||||||
	// Good connections that should be put back to the pool. They
 | 
						// Good connections that should be put back to the pool. They
 | 
				
			||||||
	// can't be put immediately, because pool.First will return them
 | 
						// can't be put immediately, because pool.First will return them
 | 
				
			||||||
	// again on next iteration.
 | 
						// again on next iteration.
 | 
				
			||||||
	cnsToPut := make([]*conn, 0)
 | 
						cnsToPut := make([]*pool.Conn, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		cn := d.pool.First()
 | 
							cn := d.pool.First()
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user