Vendor update

This commit is contained in:
Morgan Bazalgette 2017-07-25 14:58:07 +02:00
parent 8ebe5f6a02
commit e5f062ee91
No known key found for this signature in database
GPG Key ID: 40D328300D245DA5
23 changed files with 382 additions and 228 deletions

View File

@ -9,7 +9,7 @@ type OsuBool bool
// UnmarshalJSON converts `"0"` to false and `"1"` to true. // UnmarshalJSON converts `"0"` to false and `"1"` to true.
func (o *OsuBool) UnmarshalJSON(data []byte) error { func (o *OsuBool) UnmarshalJSON(data []byte) error {
if string(data) == `"0"` { if string(data) == `0` {
*o = false *o = false
return nil return nil
} }

1
vendor/gopkg.in/redis.v5/Makefile generated vendored
View File

@ -15,4 +15,5 @@ testdata/redis:
wget -qO- https://github.com/antirez/redis/archive/unstable.tar.gz | tar xvz --strip-components=1 -C $@ wget -qO- https://github.com/antirez/redis/archive/unstable.tar.gz | tar xvz --strip-components=1 -C $@
testdata/redis/src/redis-server: testdata/redis testdata/redis/src/redis-server: testdata/redis
sed -i 's/libjemalloc.a/libjemalloc.a -lrt/g' $</src/Makefile
cd $< && make all cd $< && make all

77
vendor/gopkg.in/redis.v5/cluster.go generated vendored
View File

@ -14,6 +14,7 @@ import (
) )
var errClusterNoNodes = internal.RedisError("redis: cluster has no nodes") var errClusterNoNodes = internal.RedisError("redis: cluster has no nodes")
var errNilClusterState = internal.RedisError("redis: cannot load cluster slots")
// ClusterOptions are used to configure a cluster client and should be // ClusterOptions are used to configure a cluster client and should be
// passed to NewClusterClient. // passed to NewClusterClient.
@ -355,7 +356,14 @@ func NewClusterClient(opt *ClusterOptions) *ClusterClient {
_, _ = c.nodes.Get(addr) _, _ = c.nodes.Get(addr)
} }
c.reloadSlots() // Preload cluster slots.
for i := 0; i < 10; i++ {
state, err := c.reloadSlots()
if err == nil {
c._state.Store(state)
break
}
}
if opt.IdleCheckFrequency > 0 { if opt.IdleCheckFrequency > 0 {
go c.reaper(opt.IdleCheckFrequency) go c.reaper(opt.IdleCheckFrequency)
@ -366,10 +374,11 @@ func NewClusterClient(opt *ClusterOptions) *ClusterClient {
func (c *ClusterClient) state() *clusterState { func (c *ClusterClient) state() *clusterState {
v := c._state.Load() v := c._state.Load()
if v == nil { if v != nil {
return nil
}
return v.(*clusterState) return v.(*clusterState)
}
c.lazyReloadSlots()
return nil
} }
func (c *ClusterClient) cmdSlotAndNode(state *clusterState, cmd Cmder) (int, *clusterNode, error) { func (c *ClusterClient) cmdSlotAndNode(state *clusterState, cmd Cmder) (int, *clusterNode, error) {
@ -397,10 +406,12 @@ func (c *ClusterClient) cmdSlotAndNode(state *clusterState, cmd Cmder) (int, *cl
} }
func (c *ClusterClient) Watch(fn func(*Tx) error, keys ...string) error { func (c *ClusterClient) Watch(fn func(*Tx) error, keys ...string) error {
state := c.state()
var node *clusterNode var node *clusterNode
var err error var err error
if len(keys) > 0 { if state != nil && len(keys) > 0 {
node, err = c.state().slotMasterNode(hashtag.Slot(keys[0])) node, err = state.slotMasterNode(hashtag.Slot(keys[0]))
} else { } else {
node, err = c.nodes.Random() node, err = c.nodes.Random()
} }
@ -463,8 +474,9 @@ func (c *ClusterClient) Process(cmd Cmder) error {
var addr string var addr string
moved, ask, addr = internal.IsMovedError(err) moved, ask, addr = internal.IsMovedError(err)
if moved || ask { if moved || ask {
if slot >= 0 { state := c.state()
master, _ := c.state().slotMasterNode(slot) if state != nil && slot >= 0 {
master, _ := state.slotMasterNode(slot)
if moved && (master == nil || master.Client.getAddr() != addr) { if moved && (master == nil || master.Client.getAddr() != addr) {
c.lazyReloadSlots() c.lazyReloadSlots()
} }
@ -523,7 +535,7 @@ func (c *ClusterClient) ForEachNode(fn func(client *Client) error) error {
func (c *ClusterClient) ForEachMaster(fn func(client *Client) error) error { func (c *ClusterClient) ForEachMaster(fn func(client *Client) error) error {
state := c.state() state := c.state()
if state == nil { if state == nil {
return nil return errNilClusterState
} }
var wg sync.WaitGroup var wg sync.WaitGroup
@ -564,12 +576,13 @@ func (c *ClusterClient) ForEachMaster(fn func(client *Client) error) error {
// PoolStats returns accumulated connection pool stats. // PoolStats returns accumulated connection pool stats.
func (c *ClusterClient) PoolStats() *PoolStats { func (c *ClusterClient) PoolStats() *PoolStats {
var acc PoolStats
nodes, err := c.nodes.All() nodes, err := c.nodes.All()
if err != nil { if err != nil {
return nil return &acc
} }
var acc PoolStats
for _, node := range nodes { for _, node := range nodes {
s := node.Client.connPool.Stats() s := node.Client.connPool.Stats()
acc.Requests += s.Requests acc.Requests += s.Requests
@ -585,37 +598,46 @@ func (c *ClusterClient) lazyReloadSlots() {
if !atomic.CompareAndSwapUint32(&c.reloading, 0, 1) { if !atomic.CompareAndSwapUint32(&c.reloading, 0, 1) {
return return
} }
go func() { go func() {
c.reloadSlots() for i := 0; i < 1000; i++ {
state, err := c.reloadSlots()
if err == pool.ErrClosed {
break
}
if err == nil {
c._state.Store(state)
break
}
time.Sleep(time.Millisecond)
}
time.Sleep(3 * time.Second)
atomic.StoreUint32(&c.reloading, 0) atomic.StoreUint32(&c.reloading, 0)
}() }()
} }
func (c *ClusterClient) reloadSlots() { func (c *ClusterClient) reloadSlots() (*clusterState, error) {
for i := 0; i < 10; i++ {
node, err := c.nodes.Random() node, err := c.nodes.Random()
if err != nil { if err != nil {
return return nil, err
} }
// TODO: fix race
if c.cmds == nil { if c.cmds == nil {
cmds, err := node.Client.Command().Result() cmds, err := node.Client.Command().Result()
if err == nil { if err != nil {
c.cmds = cmds return nil, err
} }
c.cmds = cmds
} }
slots, err := node.Client.ClusterSlots().Result() slots, err := node.Client.ClusterSlots().Result()
if err != nil { if err != nil {
continue return nil, err
} }
state, err := newClusterState(c.nodes, slots) return newClusterState(c.nodes, slots)
if err != nil {
return
}
c._state.Store(state)
}
} }
// reaper closes idle connections to the cluster. // reaper closes idle connections to the cluster.
@ -789,8 +811,13 @@ func (c *ClusterClient) txPipelineExec(cmds []Cmder) error {
return err return err
} }
state := c.state()
if state == nil {
return errNilClusterState
}
for slot, cmds := range cmdsMap { for slot, cmds := range cmdsMap {
node, err := c.state().slotMasterNode(slot) node, err := state.slotMasterNode(slot)
if err != nil { if err != nil {
setCmdsErr(cmds, err) setCmdsErr(cmds, err)
continue continue

View File

@ -58,7 +58,7 @@ func writeCmd(cn *pool.Conn, cmds ...Cmder) error {
} }
} }
_, err := cn.NetConn.Write(cn.Wb.Bytes()) _, err := cn.Write(cn.Wb.Bytes())
return err return err
} }
@ -445,7 +445,7 @@ func (cmd *StringCmd) Result() (string, error) {
} }
func (cmd *StringCmd) Bytes() ([]byte, error) { func (cmd *StringCmd) Bytes() ([]byte, error) {
return []byte(cmd.val), cmd.err return cmd.val, cmd.err
} }
func (cmd *StringCmd) Int64() (int64, error) { func (cmd *StringCmd) Int64() (int64, error) {
@ -542,6 +542,10 @@ func (cmd *StringSliceCmd) String() string {
return cmdString(cmd, cmd.val) return cmdString(cmd, cmd.val)
} }
func (cmd *StringSliceCmd) ScanSlice(container interface{}) error {
return proto.ScanSlice(cmd.Val(), container)
}
func (cmd *StringSliceCmd) readReply(cn *pool.Conn) error { func (cmd *StringSliceCmd) readReply(cn *pool.Conn) error {
var v interface{} var v interface{}
v, cmd.err = cn.Rd.ReadArrayReply(stringSliceParser) v, cmd.err = cn.Rd.ReadArrayReply(stringSliceParser)

64
vendor/gopkg.in/redis.v5/commands.go generated vendored
View File

@ -50,14 +50,16 @@ type Cmdable interface {
Unlink(keys ...string) *IntCmd Unlink(keys ...string) *IntCmd
Dump(key string) *StringCmd Dump(key string) *StringCmd
Exists(key string) *BoolCmd Exists(key string) *BoolCmd
// TODO: merge with Exists in v6
ExistsMulti(keys ...string) *IntCmd
Expire(key string, expiration time.Duration) *BoolCmd Expire(key string, expiration time.Duration) *BoolCmd
ExpireAt(key string, tm time.Time) *BoolCmd ExpireAt(key string, tm time.Time) *BoolCmd
Keys(pattern string) *StringSliceCmd Keys(pattern string) *StringSliceCmd
Migrate(host, port, key string, db int64, timeout time.Duration) *StatusCmd Migrate(host, port, key string, db int64, timeout time.Duration) *StatusCmd
Move(key string, db int64) *BoolCmd Move(key string, db int64) *BoolCmd
ObjectRefCount(keys ...string) *IntCmd ObjectRefCount(key string) *IntCmd
ObjectEncoding(keys ...string) *StringCmd ObjectEncoding(key string) *StringCmd
ObjectIdleTime(keys ...string) *DurationCmd ObjectIdleTime(key string) *DurationCmd
Persist(key string) *BoolCmd Persist(key string) *BoolCmd
PExpire(key string, expiration time.Duration) *BoolCmd PExpire(key string, expiration time.Duration) *BoolCmd
PExpireAt(key string, tm time.Time) *BoolCmd PExpireAt(key string, tm time.Time) *BoolCmd
@ -170,6 +172,7 @@ type Cmdable interface {
ZRem(key string, members ...interface{}) *IntCmd ZRem(key string, members ...interface{}) *IntCmd
ZRemRangeByRank(key string, start, stop int64) *IntCmd ZRemRangeByRank(key string, start, stop int64) *IntCmd
ZRemRangeByScore(key, min, max string) *IntCmd ZRemRangeByScore(key, min, max string) *IntCmd
ZRemRangeByLex(key, min, max string) *IntCmd
ZRevRange(key string, start, stop int64) *StringSliceCmd ZRevRange(key string, start, stop int64) *StringSliceCmd
ZRevRangeWithScores(key string, start, stop int64) *ZSliceCmd ZRevRangeWithScores(key string, start, stop int64) *ZSliceCmd
ZRevRangeByScore(key string, opt ZRangeBy) *StringSliceCmd ZRevRangeByScore(key string, opt ZRangeBy) *StringSliceCmd
@ -270,6 +273,13 @@ func (c *cmdable) Ping() *StatusCmd {
return cmd return cmd
} }
func (c *cmdable) Wait(numSlaves int, timeout time.Duration) *IntCmd {
cmd := NewIntCmd("wait", numSlaves, int(timeout/time.Millisecond))
c.process(cmd)
return cmd
}
func (c *cmdable) Quit() *StatusCmd { func (c *cmdable) Quit() *StatusCmd {
panic("not implemented") panic("not implemented")
} }
@ -316,6 +326,17 @@ func (c *cmdable) Exists(key string) *BoolCmd {
return cmd return cmd
} }
func (c *cmdable) ExistsMulti(keys ...string) *IntCmd {
args := make([]interface{}, 1+len(keys))
args[0] = "exists"
for i, key := range keys {
args[1+i] = key
}
cmd := NewIntCmd(args...)
c.process(cmd)
return cmd
}
func (c *cmdable) Expire(key string, expiration time.Duration) *BoolCmd { func (c *cmdable) Expire(key string, expiration time.Duration) *BoolCmd {
cmd := NewBoolCmd("expire", key, formatSec(expiration)) cmd := NewBoolCmd("expire", key, formatSec(expiration))
c.process(cmd) c.process(cmd)
@ -354,38 +375,20 @@ func (c *cmdable) Move(key string, db int64) *BoolCmd {
return cmd return cmd
} }
func (c *cmdable) ObjectRefCount(keys ...string) *IntCmd { func (c *cmdable) ObjectRefCount(key string) *IntCmd {
args := make([]interface{}, 2+len(keys)) cmd := NewIntCmd("object", "refcount", key)
args[0] = "object"
args[1] = "refcount"
for i, key := range keys {
args[2+i] = key
}
cmd := NewIntCmd(args...)
c.process(cmd) c.process(cmd)
return cmd return cmd
} }
func (c *cmdable) ObjectEncoding(keys ...string) *StringCmd { func (c *cmdable) ObjectEncoding(key string) *StringCmd {
args := make([]interface{}, 2+len(keys)) cmd := NewStringCmd("object", "encoding", key)
args[0] = "object"
args[1] = "encoding"
for i, key := range keys {
args[2+i] = key
}
cmd := NewStringCmd(args...)
c.process(cmd) c.process(cmd)
return cmd return cmd
} }
func (c *cmdable) ObjectIdleTime(keys ...string) *DurationCmd { func (c *cmdable) ObjectIdleTime(key string) *DurationCmd {
args := make([]interface{}, 2+len(keys)) cmd := NewDurationCmd(time.Second, "object", "idletime", key)
args[0] = "object"
args[1] = "idletime"
for i, key := range keys {
args[2+i] = key
}
cmd := NewDurationCmd(time.Second, args...)
c.process(cmd) c.process(cmd)
return cmd return cmd
} }
@ -731,6 +734,7 @@ func (c *cmdable) MSetNX(pairs ...interface{}) *BoolCmd {
// Redis `SET key value [expiration]` command. // Redis `SET key value [expiration]` command.
// //
// Use expiration for `SETEX`-like behavior.
// Zero expiration means the key has no expiration time. // Zero expiration means the key has no expiration time.
func (c *cmdable) Set(key string, value interface{}, expiration time.Duration) *StatusCmd { func (c *cmdable) Set(key string, value interface{}, expiration time.Duration) *StatusCmd {
args := make([]interface{}, 3, 4) args := make([]interface{}, 3, 4)
@ -1468,6 +1472,12 @@ func (c *cmdable) ZRemRangeByScore(key, min, max string) *IntCmd {
return cmd return cmd
} }
func (c *cmdable) ZRemRangeByLex(key, min, max string) *IntCmd {
cmd := NewIntCmd("zremrangebylex", key, min, max)
c.process(cmd)
return cmd
}
func (c *cmdable) ZRevRange(key string, start, stop int64) *StringSliceCmd { func (c *cmdable) ZRevRange(key string, start, stop int64) *StringSliceCmd {
cmd := NewStringSliceCmd("zrevrange", key, start, stop) cmd := NewStringSliceCmd("zrevrange", key, start, stop)
c.process(cmd) c.process(cmd)

View File

@ -2,56 +2,77 @@ package pool
import ( import (
"net" "net"
"sync/atomic"
"time" "time"
"gopkg.in/redis.v5/internal/proto" "gopkg.in/redis.v5/internal/proto"
) )
const defaultBufSize = 4096
var noDeadline = time.Time{} var noDeadline = time.Time{}
type Conn struct { type Conn struct {
NetConn net.Conn netConn net.Conn
Rd *proto.Reader Rd *proto.Reader
Wb *proto.WriteBuffer Wb *proto.WriteBuffer
Inited bool Inited bool
UsedAt time.Time usedAt atomic.Value
} }
func NewConn(netConn net.Conn) *Conn { func NewConn(netConn net.Conn) *Conn {
cn := &Conn{ cn := &Conn{
NetConn: netConn, netConn: netConn,
Wb: proto.NewWriteBuffer(), Wb: proto.NewWriteBuffer(),
UsedAt: time.Now(),
} }
cn.Rd = proto.NewReader(cn.NetConn) cn.Rd = proto.NewReader(cn.netConn)
cn.SetUsedAt(time.Now())
return cn return cn
} }
func (cn *Conn) UsedAt() time.Time {
return cn.usedAt.Load().(time.Time)
}
func (cn *Conn) SetUsedAt(tm time.Time) {
cn.usedAt.Store(tm)
}
func (cn *Conn) SetNetConn(netConn net.Conn) {
cn.netConn = netConn
cn.Rd.Reset(netConn)
}
func (cn *Conn) IsStale(timeout time.Duration) bool { func (cn *Conn) IsStale(timeout time.Duration) bool {
return timeout > 0 && time.Since(cn.UsedAt) > timeout return timeout > 0 && time.Since(cn.UsedAt()) > timeout
} }
func (cn *Conn) SetReadTimeout(timeout time.Duration) error { func (cn *Conn) SetReadTimeout(timeout time.Duration) error {
cn.UsedAt = time.Now() now := time.Now()
cn.SetUsedAt(now)
if timeout > 0 { if timeout > 0 {
return cn.NetConn.SetReadDeadline(cn.UsedAt.Add(timeout)) return cn.netConn.SetReadDeadline(now.Add(timeout))
} }
return cn.NetConn.SetReadDeadline(noDeadline) return cn.netConn.SetReadDeadline(noDeadline)
} }
func (cn *Conn) SetWriteTimeout(timeout time.Duration) error { func (cn *Conn) SetWriteTimeout(timeout time.Duration) error {
cn.UsedAt = time.Now() now := time.Now()
cn.SetUsedAt(now)
if timeout > 0 { if timeout > 0 {
return cn.NetConn.SetWriteDeadline(cn.UsedAt.Add(timeout)) return cn.netConn.SetWriteDeadline(now.Add(timeout))
} }
return cn.NetConn.SetWriteDeadline(noDeadline) return cn.netConn.SetWriteDeadline(noDeadline)
}
func (cn *Conn) Write(b []byte) (int, error) {
return cn.netConn.Write(b)
}
func (cn *Conn) RemoteAddr() net.Addr {
return cn.netConn.RemoteAddr()
} }
func (cn *Conn) Close() error { func (cn *Conn) Close() error {
return cn.NetConn.Close() return cn.netConn.Close()
} }

View File

@ -19,7 +19,9 @@ var (
var timers = sync.Pool{ var timers = sync.Pool{
New: func() interface{} { New: func() interface{} {
return time.NewTimer(0) t := time.NewTimer(time.Hour)
t.Stop()
return t
}, },
} }
@ -41,7 +43,6 @@ type Pooler interface {
FreeLen() int FreeLen() int
Stats() *Stats Stats() *Stats
Close() error Close() error
Closed() bool
} }
type dialer func() (net.Conn, error) type dialer func() (net.Conn, error)
@ -96,12 +97,13 @@ func (p *ConnPool) NewConn() (*Conn, error) {
func (p *ConnPool) PopFree() *Conn { func (p *ConnPool) PopFree() *Conn {
timer := timers.Get().(*time.Timer) timer := timers.Get().(*time.Timer)
if !timer.Reset(p.poolTimeout) { timer.Reset(p.poolTimeout)
<-timer.C
}
select { select {
case p.queue <- struct{}{}: case p.queue <- struct{}{}:
if !timer.Stop() {
<-timer.C
}
timers.Put(timer) timers.Put(timer)
case <-timer.C: case <-timer.C:
timers.Put(timer) timers.Put(timer)
@ -132,19 +134,20 @@ func (p *ConnPool) popFree() *Conn {
// Get returns existed connection from the pool or creates a new one. // Get returns existed connection from the pool or creates a new one.
func (p *ConnPool) Get() (*Conn, bool, error) { func (p *ConnPool) Get() (*Conn, bool, error) {
if p.Closed() { if p.closed() {
return nil, false, ErrClosed return nil, false, ErrClosed
} }
atomic.AddUint32(&p.stats.Requests, 1) atomic.AddUint32(&p.stats.Requests, 1)
timer := timers.Get().(*time.Timer) timer := timers.Get().(*time.Timer)
if !timer.Reset(p.poolTimeout) { timer.Reset(p.poolTimeout)
<-timer.C
}
select { select {
case p.queue <- struct{}{}: case p.queue <- struct{}{}:
if !timer.Stop() {
<-timer.C
}
timers.Put(timer) timers.Put(timer)
case <-timer.C: case <-timer.C:
timers.Put(timer) timers.Put(timer)
@ -241,7 +244,7 @@ func (p *ConnPool) Stats() *Stats {
} }
} }
func (p *ConnPool) Closed() bool { func (p *ConnPool) closed() bool {
return atomic.LoadInt32(&p._closed) == 1 return atomic.LoadInt32(&p._closed) == 1
} }
@ -318,7 +321,7 @@ func (p *ConnPool) reaper(frequency time.Duration) {
defer ticker.Stop() defer ticker.Stop()
for _ = range ticker.C { for _ = range ticker.C {
if p.Closed() { if p.closed() {
break break
} }
n, err := p.ReapStaleConns() n, err := p.ReapStaleConns()

View File

@ -12,10 +12,6 @@ func NewSingleConnPool(cn *Conn) *SingleConnPool {
} }
} }
func (p *SingleConnPool) First() *Conn {
return p.cn
}
func (p *SingleConnPool) Get() (*Conn, bool, error) { func (p *SingleConnPool) Get() (*Conn, bool, error) {
return p.cn, false, nil return p.cn, false, nil
} }
@ -49,7 +45,3 @@ func (p *SingleConnPool) Stats() *Stats {
func (p *SingleConnPool) Close() error { func (p *SingleConnPool) Close() error {
return nil return nil
} }
func (p *SingleConnPool) Closed() bool {
return false
}

View File

@ -11,7 +11,7 @@ type StickyConnPool struct {
cn *Conn cn *Conn
closed bool closed bool
mx sync.Mutex mu sync.Mutex
} }
var _ Pooler = (*StickyConnPool)(nil) var _ Pooler = (*StickyConnPool)(nil)
@ -23,16 +23,9 @@ func NewStickyConnPool(pool *ConnPool, reusable bool) *StickyConnPool {
} }
} }
func (p *StickyConnPool) First() *Conn {
p.mx.Lock()
cn := p.cn
p.mx.Unlock()
return cn
}
func (p *StickyConnPool) Get() (*Conn, bool, error) { func (p *StickyConnPool) Get() (*Conn, bool, error) {
defer p.mx.Unlock() p.mu.Lock()
p.mx.Lock() defer p.mu.Unlock()
if p.closed { if p.closed {
return nil, false, ErrClosed return nil, false, ErrClosed
@ -56,14 +49,12 @@ func (p *StickyConnPool) putUpstream() (err error) {
} }
func (p *StickyConnPool) Put(cn *Conn) error { func (p *StickyConnPool) Put(cn *Conn) error {
defer p.mx.Unlock() p.mu.Lock()
p.mx.Lock() defer p.mu.Unlock()
if p.closed { if p.closed {
return ErrClosed return ErrClosed
} }
if p.cn != cn {
panic("p.cn != cn")
}
return nil return nil
} }
@ -74,23 +65,19 @@ func (p *StickyConnPool) removeUpstream(reason error) error {
} }
func (p *StickyConnPool) Remove(cn *Conn, reason error) error { func (p *StickyConnPool) Remove(cn *Conn, reason error) error {
defer p.mx.Unlock() p.mu.Lock()
p.mx.Lock() defer p.mu.Unlock()
if p.closed { if p.closed {
return nil return nil
} }
if p.cn == nil {
panic("p.cn == nil")
}
if cn != nil && p.cn != cn {
panic("p.cn != cn")
}
return p.removeUpstream(reason) return p.removeUpstream(reason)
} }
func (p *StickyConnPool) Len() int { func (p *StickyConnPool) Len() int {
defer p.mx.Unlock() p.mu.Lock()
p.mx.Lock() defer p.mu.Unlock()
if p.cn == nil { if p.cn == nil {
return 0 return 0
} }
@ -98,8 +85,9 @@ func (p *StickyConnPool) Len() int {
} }
func (p *StickyConnPool) FreeLen() int { func (p *StickyConnPool) FreeLen() int {
defer p.mx.Unlock() p.mu.Lock()
p.mx.Lock() defer p.mu.Unlock()
if p.cn == nil { if p.cn == nil {
return 1 return 1
} }
@ -111,8 +99,9 @@ func (p *StickyConnPool) Stats() *Stats {
} }
func (p *StickyConnPool) Close() error { func (p *StickyConnPool) Close() error {
defer p.mx.Unlock() p.mu.Lock()
p.mx.Lock() defer p.mu.Unlock()
if p.closed { if p.closed {
return ErrClosed return ErrClosed
} }
@ -128,10 +117,3 @@ func (p *StickyConnPool) Close() error {
} }
return err return err
} }
func (p *StickyConnPool) Closed() bool {
p.mx.Lock()
closed := p.closed
p.mx.Unlock()
return closed
}

View File

@ -29,10 +29,14 @@ type Reader struct {
func NewReader(rd io.Reader) *Reader { func NewReader(rd io.Reader) *Reader {
return &Reader{ return &Reader{
src: bufio.NewReader(rd), src: bufio.NewReader(rd),
buf: make([]byte, 0, bufferSize), buf: make([]byte, 4096),
} }
} }
func (r *Reader) Reset(rd io.Reader) {
r.src.Reset(rd)
}
func (p *Reader) PeekBuffered() []byte { func (p *Reader) PeekBuffered() []byte {
if n := p.src.Buffered(); n != 0 { if n := p.src.Buffered(); n != 0 {
b, _ := p.src.Peek(n) b, _ := p.src.Peek(n)
@ -42,7 +46,12 @@ func (p *Reader) PeekBuffered() []byte {
} }
func (p *Reader) ReadN(n int) ([]byte, error) { func (p *Reader) ReadN(n int) ([]byte, error) {
return readN(p.src, p.buf, n) b, err := readN(p.src, p.buf, n)
if err != nil {
return nil, err
}
p.buf = b
return b, nil
} }
func (p *Reader) ReadLine() ([]byte, error) { func (p *Reader) ReadLine() ([]byte, error) {
@ -72,11 +81,11 @@ func (p *Reader) ReadReply(m MultiBulkParse) (interface{}, error) {
case ErrorReply: case ErrorReply:
return nil, ParseErrorReply(line) return nil, ParseErrorReply(line)
case StatusReply: case StatusReply:
return parseStatusValue(line) return parseStatusValue(line), nil
case IntReply: case IntReply:
return parseInt(line[1:], 10, 64) return parseInt(line[1:], 10, 64)
case StringReply: case StringReply:
return p.readBytesValue(line) return p.readTmpBytesValue(line)
case ArrayReply: case ArrayReply:
n, err := parseArrayLen(line) n, err := parseArrayLen(line)
if err != nil { if err != nil {
@ -111,9 +120,9 @@ func (p *Reader) ReadTmpBytesReply() ([]byte, error) {
case ErrorReply: case ErrorReply:
return nil, ParseErrorReply(line) return nil, ParseErrorReply(line)
case StringReply: case StringReply:
return p.readBytesValue(line) return p.readTmpBytesValue(line)
case StatusReply: case StatusReply:
return parseStatusValue(line) return parseStatusValue(line), nil
default: default:
return nil, fmt.Errorf("redis: can't parse string reply: %.100q", line) return nil, fmt.Errorf("redis: can't parse string reply: %.100q", line)
} }
@ -210,7 +219,7 @@ func (p *Reader) ReadScanReply() ([]string, uint64, error) {
return keys, cursor, err return keys, cursor, err
} }
func (p *Reader) readBytesValue(line []byte) ([]byte, error) { func (p *Reader) readTmpBytesValue(line []byte) ([]byte, error) {
if isNilReply(line) { if isNilReply(line) {
return nil, internal.Nil return nil, internal.Nil
} }
@ -297,8 +306,8 @@ func ParseErrorReply(line []byte) error {
return internal.RedisError(string(line[1:])) return internal.RedisError(string(line[1:]))
} }
func parseStatusValue(line []byte) ([]byte, error) { func parseStatusValue(line []byte) []byte {
return line[1:], nil return line[1:]
} }
func parseArrayLen(line []byte) (int64, error) { func parseArrayLen(line []byte) (int64, error) {

View File

@ -3,6 +3,7 @@ package proto
import ( import (
"encoding" "encoding"
"fmt" "fmt"
"reflect"
"gopkg.in/redis.v5/internal" "gopkg.in/redis.v5/internal"
) )
@ -105,3 +106,26 @@ func Scan(b []byte, v interface{}) error {
"redis: can't unmarshal %T (consider implementing BinaryUnmarshaler)", v) "redis: can't unmarshal %T (consider implementing BinaryUnmarshaler)", v)
} }
} }
func ScanSlice(data []string, slice interface{}) error {
v := reflect.ValueOf(slice)
if !v.IsValid() {
return fmt.Errorf("redis: ScanSlice(nil)")
}
if v.Kind() != reflect.Ptr {
return fmt.Errorf("redis: ScanSlice(non-pointer %T)", slice)
}
v = v.Elem()
if v.Kind() != reflect.Slice {
return fmt.Errorf("redis: ScanSlice(non-slice %T)", slice)
}
for i, s := range data {
elem := internal.SliceNextElem(v)
if err := Scan([]byte(s), elem.Addr().Interface()); err != nil {
return fmt.Errorf("redis: ScanSlice(index=%d value=%q) failed: %s", i, s, err)
}
}
return nil
}

View File

@ -8,11 +8,13 @@ import (
const bufferSize = 4096 const bufferSize = 4096
type WriteBuffer struct{ b []byte } type WriteBuffer struct {
b []byte
}
func NewWriteBuffer() *WriteBuffer { func NewWriteBuffer() *WriteBuffer {
return &WriteBuffer{ return &WriteBuffer{
b: make([]byte, 0, bufferSize), b: make([]byte, 0, 4096),
} }
} }

View File

@ -1,5 +1,7 @@
package internal package internal
import "reflect"
func ToLower(s string) string { func ToLower(s string) string {
if isLower(s) { if isLower(s) {
return s return s
@ -25,3 +27,21 @@ func isLower(s string) bool {
} }
return true return true
} }
func SliceNextElem(v reflect.Value) reflect.Value {
if v.Len() < v.Cap() {
v.Set(v.Slice(0, v.Len()+1))
return v.Index(v.Len() - 1)
}
elemType := v.Type().Elem()
if elemType.Kind() == reflect.Ptr {
elem := reflect.New(elemType.Elem())
v.Set(reflect.Append(v, elem))
return elem.Elem()
}
v.Set(reflect.Append(v, reflect.Zero(elemType)))
return v.Index(v.Len() - 1)
}

View File

@ -3,6 +3,7 @@ package redis
import ( import (
"crypto/tls" "crypto/tls"
"errors" "errors"
"fmt"
"net" "net"
"net/url" "net/url"
"strconv" "strconv"
@ -151,7 +152,7 @@ func ParseURL(redisURL string) (*Options, error) {
o.DB = 0 o.DB = 0
case 1: case 1:
if o.DB, err = strconv.Atoi(f[0]); err != nil { if o.DB, err = strconv.Atoi(f[0]); err != nil {
return nil, errors.New("invalid redis database number: " + err.Error()) return nil, fmt.Errorf("invalid redis database number: %q", f[0])
} }
default: default:
return nil, errors.New("invalid redis URL path: " + u.Path) return nil, errors.New("invalid redis URL path: " + u.Path)

View File

@ -61,8 +61,8 @@ func (c *Pipeline) discard() error {
// Exec always returns list of commands and error of the first failed // Exec always returns list of commands and error of the first failed
// command if any. // command if any.
func (c *Pipeline) Exec() ([]Cmder, error) { func (c *Pipeline) Exec() ([]Cmder, error) {
defer c.mu.Unlock()
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock()
if c.closed { if c.closed {
return nil, pool.ErrClosed return nil, pool.ErrClosed

39
vendor/gopkg.in/redis.v5/pubsub.go generated vendored
View File

@ -3,6 +3,7 @@ package redis
import ( import (
"fmt" "fmt"
"net" "net"
"sync"
"time" "time"
"gopkg.in/redis.v5/internal" "gopkg.in/redis.v5/internal"
@ -14,7 +15,9 @@ import (
// multiple goroutines. // multiple goroutines.
type PubSub struct { type PubSub struct {
base baseClient base baseClient
cmd *Cmd
mu sync.Mutex
channels []string channels []string
patterns []string patterns []string
} }
@ -95,10 +98,10 @@ func (c *PubSub) Close() error {
return c.base.Close() return c.base.Close()
} }
func (c *PubSub) Ping(payload string) error { func (c *PubSub) Ping(payload ...string) error {
args := []interface{}{"PING"} args := []interface{}{"PING"}
if payload != "" { if len(payload) == 1 {
args = append(args, payload) args = append(args, payload[0])
} }
cmd := NewCmd(args...) cmd := NewCmd(args...)
@ -150,7 +153,13 @@ func (p *Pong) String() string {
return "Pong" return "Pong"
} }
func (c *PubSub) newMessage(reply []interface{}) (interface{}, error) { func (c *PubSub) newMessage(reply interface{}) (interface{}, error) {
switch reply := reply.(type) {
case string:
return &Pong{
Payload: reply,
}, nil
case []interface{}:
switch kind := reply[0].(string); kind { switch kind := reply[0].(string); kind {
case "subscribe", "unsubscribe", "psubscribe", "punsubscribe": case "subscribe", "unsubscribe", "psubscribe", "punsubscribe":
return &Subscription{ return &Subscription{
@ -174,7 +183,10 @@ func (c *PubSub) newMessage(reply []interface{}) (interface{}, error) {
Payload: reply[1].(string), Payload: reply[1].(string),
}, nil }, nil
default: default:
return nil, fmt.Errorf("redis: unsupported pubsub notification: %q", kind) return nil, fmt.Errorf("redis: unsupported pubsub message: %q", kind)
}
default:
return nil, fmt.Errorf("redis: unsupported pubsub message: %#v", reply)
} }
} }
@ -182,7 +194,9 @@ func (c *PubSub) newMessage(reply []interface{}) (interface{}, error) {
// is not received in time. This is low-level API and most clients // is not received in time. This is low-level API and most clients
// should use ReceiveMessage. // should use ReceiveMessage.
func (c *PubSub) ReceiveTimeout(timeout time.Duration) (interface{}, error) { func (c *PubSub) ReceiveTimeout(timeout time.Duration) (interface{}, error) {
cmd := NewSliceCmd() if c.cmd == nil {
c.cmd = NewCmd()
}
cn, _, err := c.conn() cn, _, err := c.conn()
if err != nil { if err != nil {
@ -190,13 +204,13 @@ func (c *PubSub) ReceiveTimeout(timeout time.Duration) (interface{}, error) {
} }
cn.SetReadTimeout(timeout) cn.SetReadTimeout(timeout)
err = cmd.readReply(cn) err = c.cmd.readReply(cn)
c.putConn(cn, err) c.putConn(cn, err)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return c.newMessage(cmd.Val()) return c.newMessage(c.cmd.Val())
} }
// Receive returns a message as a Subscription, Message, Pong or error. // Receive returns a message as a Subscription, Message, Pong or error.
@ -225,14 +239,14 @@ func (c *PubSub) receiveMessage(timeout time.Duration) (*Message, error) {
errNum++ errNum++
if errNum < 3 { if errNum < 3 {
if netErr, ok := err.(net.Error); ok && netErr.Timeout() { if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
err := c.Ping("") err := c.Ping()
if err != nil { if err != nil {
internal.Logf("PubSub.Ping failed: %s", err) internal.Logf("PubSub.Ping failed: %s", err)
} }
} }
} else { } else {
// 3 consequent errors - connection is bad // 3 consequent errors - connection is broken or
// and/or Redis Server is down. // Redis Server is down.
// Sleep to not exceed max number of open connections. // Sleep to not exceed max number of open connections.
time.Sleep(time.Second) time.Sleep(time.Second)
} }
@ -256,9 +270,6 @@ func (c *PubSub) receiveMessage(timeout time.Duration) (*Message, error) {
} }
func (c *PubSub) resubscribe() { func (c *PubSub) resubscribe() {
if c.base.closed() {
return
}
if len(c.channels) > 0 { if len(c.channels) > 0 {
if err := c.Subscribe(c.channels...); err != nil { if err := c.Subscribe(c.channels...); err != nil {
internal.Logf("Subscribe failed: %s", err) internal.Logf("Subscribe failed: %s", err)

19
vendor/gopkg.in/redis.v5/redis.go generated vendored
View File

@ -17,14 +17,6 @@ func SetLogger(logger *log.Logger) {
internal.Logger = logger internal.Logger = logger
} }
type baseClient struct {
connPool pool.Pooler
opt *Options
process func(Cmder) error
onClose func() error // hook called when client is closed
}
func (c *baseClient) String() string { func (c *baseClient) String() string {
return fmt.Sprintf("Redis<%s db:%d>", c.getAddr(), c.opt.DB) return fmt.Sprintf("Redis<%s db:%d>", c.getAddr(), c.opt.DB)
} }
@ -134,10 +126,6 @@ func (c *baseClient) cmdTimeout(cmd Cmder) time.Duration {
} }
} }
func (c *baseClient) closed() bool {
return c.connPool.Closed()
}
// Close closes the client, releasing any open resources. // Close closes the client, releasing any open resources.
// //
// It is rare to Close a Client, as the Client is meant to be // It is rare to Close a Client, as the Client is meant to be
@ -309,6 +297,13 @@ func NewClient(opt *Options) *Client {
return newClient(opt, newConnPool(opt)) return newClient(opt, newConnPool(opt))
} }
func (c *Client) copy() *Client {
c2 := new(Client)
*c2 = *c
c2.cmdable.process = c2.Process
return c2
}
// PoolStats returns connection pool stats. // PoolStats returns connection pool stats.
func (c *Client) PoolStats() *PoolStats { func (c *Client) PoolStats() *PoolStats {
s := c.connPool.Stats() s := c.connPool.Stats()

35
vendor/gopkg.in/redis.v5/redis_context.go generated vendored Normal file
View File

@ -0,0 +1,35 @@
// +build go1.7
package redis
import (
"context"
"gopkg.in/redis.v5/internal/pool"
)
type baseClient struct {
connPool pool.Pooler
opt *Options
process func(Cmder) error
onClose func() error // hook called when client is closed
ctx context.Context
}
func (c *Client) Context() context.Context {
if c.ctx != nil {
return c.ctx
}
return context.Background()
}
func (c *Client) WithContext(ctx context.Context) *Client {
if ctx == nil {
panic("nil context")
}
c2 := c.copy()
c2.ctx = ctx
return c2
}

15
vendor/gopkg.in/redis.v5/redis_no_context.go generated vendored Normal file
View File

@ -0,0 +1,15 @@
// +build !go1.7
package redis
import (
"gopkg.in/redis.v5/internal/pool"
)
type baseClient struct {
connPool pool.Pooler
opt *Options
process func(Cmder) error
onClose func() error // hook called when client is closed
}

2
vendor/gopkg.in/redis.v5/ring.go generated vendored
View File

@ -328,8 +328,8 @@ func (c *Ring) heartbeat() {
// It is rare to Close a Ring, as the Ring is meant to be long-lived // It is rare to Close a Ring, as the Ring is meant to be long-lived
// and shared between many goroutines. // and shared between many goroutines.
func (c *Ring) Close() error { func (c *Ring) Close() error {
defer c.mu.Unlock()
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock()
if c.closed { if c.closed {
return nil return nil

10
vendor/gopkg.in/redis.v5/sentinel.go generated vendored
View File

@ -162,8 +162,8 @@ func (d *sentinelFailover) Pool() *pool.ConnPool {
} }
func (d *sentinelFailover) MasterAddr() (string, error) { func (d *sentinelFailover) MasterAddr() (string, error) {
defer d.mu.Unlock()
d.mu.Lock() d.mu.Lock()
defer d.mu.Unlock()
// Try last working sentinel. // Try last working sentinel.
if d.sentinel != nil { if d.sentinel != nil {
@ -258,7 +258,7 @@ func (d *sentinelFailover) discoverSentinels(sentinel *sentinelClient) {
// closeOldConns closes connections to the old master after failover switch. // closeOldConns closes connections to the old master after failover switch.
func (d *sentinelFailover) closeOldConns(newMaster string) { 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.PopFree will return them
// again on next iteration. // again on next iteration.
cnsToPut := make([]*pool.Conn, 0) cnsToPut := make([]*pool.Conn, 0)
@ -267,10 +267,10 @@ func (d *sentinelFailover) closeOldConns(newMaster string) {
if cn == nil { if cn == nil {
break break
} }
if cn.NetConn.RemoteAddr().String() != newMaster { if cn.RemoteAddr().String() != newMaster {
err := fmt.Errorf( err := fmt.Errorf(
"sentinel: closing connection to the old master %s", "sentinel: closing connection to the old master %s",
cn.NetConn.RemoteAddr(), cn.RemoteAddr(),
) )
internal.Logf(err.Error()) internal.Logf(err.Error())
d.pool.Remove(cn, err) d.pool.Remove(cn, err)
@ -289,8 +289,10 @@ func (d *sentinelFailover) listen(sentinel *sentinelClient) {
for { for {
if pubsub == nil { if pubsub == nil {
pubsub = sentinel.PubSub() pubsub = sentinel.PubSub()
if err := pubsub.Subscribe("+switch-master"); err != nil { if err := pubsub.Subscribe("+switch-master"); err != nil {
internal.Logf("sentinel: Subscribe failed: %s", err) internal.Logf("sentinel: Subscribe failed: %s", err)
pubsub.Close()
d.resetSentinel() d.resetSentinel()
return return
} }

View File

@ -9,7 +9,7 @@ type OsuBool bool
// UnmarshalJSON converts `"0"` to false and `"1"` to true. // UnmarshalJSON converts `"0"` to false and `"1"` to true.
func (o *OsuBool) UnmarshalJSON(data []byte) error { func (o *OsuBool) UnmarshalJSON(data []byte) error {
if string(data) == `"0"` { if string(data) == `0` {
*o = false *o = false
return nil return nil
} }

44
vendor/vendor.json vendored
View File

@ -99,10 +99,10 @@
"revisionTime": "2016-10-10T15:00:23Z" "revisionTime": "2016-10-10T15:00:23Z"
}, },
{ {
"checksumSHA1": "kUH66xItMsO3QDibGHCWZHa9B3o=", "checksumSHA1": "9ujFdggUmv6hyAgJ+Bjugh0UZAc=",
"path": "github.com/thehowl/go-osuapi", "path": "github.com/thehowl/go-osuapi",
"revision": "77ef7867f23cd52e80dcf97e62447d3b36b1d26a", "revision": "23480db9e43c9a8080cbb681ceaed855e5fca7f3",
"revisionTime": "2016-10-17T20:25:41Z" "revisionTime": "2017-03-12T09:17:38Z"
}, },
{ {
"checksumSHA1": "LTOa3BADhwvT0wFCknPueQALm8I=", "checksumSHA1": "LTOa3BADhwvT0wFCknPueQALm8I=",
@ -141,46 +141,46 @@
"revisionTime": "2017-01-10T16:23:43Z" "revisionTime": "2017-01-10T16:23:43Z"
}, },
{ {
"checksumSHA1": "4+WlWTIczvywOonXZGH34YdCf6s=", "checksumSHA1": "OU/wHTJqhyQfyRnXMVWx1Ox06kQ=",
"path": "gopkg.in/redis.v5", "path": "gopkg.in/redis.v5",
"revision": "6da05abbaa03e90e5878f0ab711478698609fe96", "revision": "a16aeec10ff407b1e7be6dd35797ccf5426ef0f0",
"revisionTime": "2017-01-13T11:52:40Z" "revisionTime": "2017-03-04T11:38:25Z"
}, },
{ {
"checksumSHA1": "MQyhe1N+NO0+uyJiEc6M2WGfb3s=", "checksumSHA1": "efyYmNqK7vcPhXW4KXfwbdA1wr4=",
"path": "gopkg.in/redis.v5/internal", "path": "gopkg.in/redis.v5/internal",
"revision": "6da05abbaa03e90e5878f0ab711478698609fe96", "revision": "a16aeec10ff407b1e7be6dd35797ccf5426ef0f0",
"revisionTime": "2017-01-13T11:52:40Z" "revisionTime": "2017-03-04T11:38:25Z"
}, },
{ {
"checksumSHA1": "2Ek4SixeRSKOX3mUiBMs3Aw+Guc=", "checksumSHA1": "2Ek4SixeRSKOX3mUiBMs3Aw+Guc=",
"path": "gopkg.in/redis.v5/internal/consistenthash", "path": "gopkg.in/redis.v5/internal/consistenthash",
"revision": "6da05abbaa03e90e5878f0ab711478698609fe96", "revision": "a16aeec10ff407b1e7be6dd35797ccf5426ef0f0",
"revisionTime": "2017-01-13T11:52:40Z" "revisionTime": "2017-03-04T11:38:25Z"
}, },
{ {
"checksumSHA1": "rJYVKcBrwYUGl7nuuusmZGrt8mY=", "checksumSHA1": "rJYVKcBrwYUGl7nuuusmZGrt8mY=",
"path": "gopkg.in/redis.v5/internal/hashtag", "path": "gopkg.in/redis.v5/internal/hashtag",
"revision": "6da05abbaa03e90e5878f0ab711478698609fe96", "revision": "a16aeec10ff407b1e7be6dd35797ccf5426ef0f0",
"revisionTime": "2017-01-13T11:52:40Z" "revisionTime": "2017-03-04T11:38:25Z"
}, },
{ {
"checksumSHA1": "yLZQUKNxXzwGlzfXhWC0Mwme2nw=", "checksumSHA1": "zsH5BF9qc31R7eEEVYLsjbIigDQ=",
"path": "gopkg.in/redis.v5/internal/pool", "path": "gopkg.in/redis.v5/internal/pool",
"revision": "6da05abbaa03e90e5878f0ab711478698609fe96", "revision": "a16aeec10ff407b1e7be6dd35797ccf5426ef0f0",
"revisionTime": "2017-01-13T11:52:40Z" "revisionTime": "2017-03-04T11:38:25Z"
}, },
{ {
"checksumSHA1": "KoSfO3h/KmlaGphIM4KzBGm65O4=", "checksumSHA1": "EqPdu5g8NhzxQOMCvzbreTQlzVE=",
"path": "gopkg.in/redis.v5/internal/proto", "path": "gopkg.in/redis.v5/internal/proto",
"revision": "6da05abbaa03e90e5878f0ab711478698609fe96", "revision": "a16aeec10ff407b1e7be6dd35797ccf5426ef0f0",
"revisionTime": "2017-01-13T11:52:40Z" "revisionTime": "2017-03-04T11:38:25Z"
}, },
{ {
"checksumSHA1": "IVLVpxH6lCjsiVVIl/qf8ftjXbg=", "checksumSHA1": "va1m3wm/nxA5IEC/3r4GQeT/+Ro=",
"path": "gopkg.in/thehowl/go-osuapi.v1", "path": "gopkg.in/thehowl/go-osuapi.v1",
"revision": "77ef7867f23cd52e80dcf97e62447d3b36b1d26a", "revision": "23480db9e43c9a8080cbb681ceaed855e5fca7f3",
"revisionTime": "2016-10-17T20:25:41Z" "revisionTime": "2017-03-12T09:17:38Z"
}, },
{ {
"checksumSHA1": "SI9tgNMlnMhxP7t6cAGuDjvoAHg=", "checksumSHA1": "SI9tgNMlnMhxP7t6cAGuDjvoAHg=",