Depenancy upgrades and movign to dep. (#8630)

Этот коммит содержится в:
Christopher Speller
2018-04-16 05:37:14 -07:00
коммит произвёл Joram Wilander
родитель bf24f51c4e
Коммит 6e2cb00008
5345 изменённых файлов: 17051 добавлений и 1634753 удалений

1
vendor/github.com/go-redis/redis/.travis.yml сгенерированный поставляемый
Просмотреть файл

@@ -8,6 +8,7 @@ go:
- 1.7.x
- 1.8.x
- 1.9.x
- 1.10.x
- tip
matrix:

3
vendor/github.com/go-redis/redis/Makefile сгенерированный поставляемый
Просмотреть файл

@@ -1,6 +1,7 @@
all: testdeps
go test ./...
go test ./... -short -race
env GOOS=linux GOARCH=386 go test ./...
go vet
testdeps: testdata/redis/src/redis-server
@@ -15,5 +16,5 @@ testdata/redis:
wget -qO- https://github.com/antirez/redis/archive/unstable.tar.gz | tar xvz --strip-components=1 -C $@
testdata/redis/src/redis-server: testdata/redis
sed -i 's/libjemalloc.a/libjemalloc.a -lrt/g' $</src/Makefile
sed -i.bak 's/libjemalloc.a/libjemalloc.a -lrt/g' $</src/Makefile
cd $< && make all

216
vendor/github.com/go-redis/redis/bench_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,216 +0,0 @@
package redis_test
import (
"bytes"
"testing"
"time"
"github.com/go-redis/redis"
)
func benchmarkRedisClient(poolSize int) *redis.Client {
client := redis.NewClient(&redis.Options{
Addr: ":6379",
DialTimeout: time.Second,
ReadTimeout: time.Second,
WriteTimeout: time.Second,
PoolSize: poolSize,
})
if err := client.FlushDB().Err(); err != nil {
panic(err)
}
return client
}
func BenchmarkRedisPing(b *testing.B) {
client := benchmarkRedisClient(10)
defer client.Close()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if err := client.Ping().Err(); err != nil {
b.Fatal(err)
}
}
})
}
func BenchmarkRedisSetString(b *testing.B) {
client := benchmarkRedisClient(10)
defer client.Close()
value := string(bytes.Repeat([]byte{'1'}, 10000))
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if err := client.Set("key", value, 0).Err(); err != nil {
b.Fatal(err)
}
}
})
}
func BenchmarkRedisGetNil(b *testing.B) {
client := benchmarkRedisClient(10)
defer client.Close()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if err := client.Get("key").Err(); err != redis.Nil {
b.Fatal(err)
}
}
})
}
func benchmarkSetRedis(b *testing.B, poolSize, payloadSize int) {
client := benchmarkRedisClient(poolSize)
defer client.Close()
value := string(bytes.Repeat([]byte{'1'}, payloadSize))
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if err := client.Set("key", value, 0).Err(); err != nil {
b.Fatal(err)
}
}
})
}
func BenchmarkSetRedis10Conns64Bytes(b *testing.B) {
benchmarkSetRedis(b, 10, 64)
}
func BenchmarkSetRedis100Conns64Bytes(b *testing.B) {
benchmarkSetRedis(b, 100, 64)
}
func BenchmarkSetRedis10Conns1KB(b *testing.B) {
benchmarkSetRedis(b, 10, 1024)
}
func BenchmarkSetRedis100Conns1KB(b *testing.B) {
benchmarkSetRedis(b, 100, 1024)
}
func BenchmarkSetRedis10Conns10KB(b *testing.B) {
benchmarkSetRedis(b, 10, 10*1024)
}
func BenchmarkSetRedis100Conns10KB(b *testing.B) {
benchmarkSetRedis(b, 100, 10*1024)
}
func BenchmarkSetRedis10Conns1MB(b *testing.B) {
benchmarkSetRedis(b, 10, 1024*1024)
}
func BenchmarkSetRedis100Conns1MB(b *testing.B) {
benchmarkSetRedis(b, 100, 1024*1024)
}
func BenchmarkRedisSetGetBytes(b *testing.B) {
client := benchmarkRedisClient(10)
defer client.Close()
value := bytes.Repeat([]byte{'1'}, 10000)
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if err := client.Set("key", value, 0).Err(); err != nil {
b.Fatal(err)
}
got, err := client.Get("key").Bytes()
if err != nil {
b.Fatal(err)
}
if !bytes.Equal(got, value) {
b.Fatalf("got != value")
}
}
})
}
func BenchmarkRedisMGet(b *testing.B) {
client := benchmarkRedisClient(10)
defer client.Close()
if err := client.MSet("key1", "hello1", "key2", "hello2").Err(); err != nil {
b.Fatal(err)
}
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if err := client.MGet("key1", "key2").Err(); err != nil {
b.Fatal(err)
}
}
})
}
func BenchmarkSetExpire(b *testing.B) {
client := benchmarkRedisClient(10)
defer client.Close()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if err := client.Set("key", "hello", 0).Err(); err != nil {
b.Fatal(err)
}
if err := client.Expire("key", time.Second).Err(); err != nil {
b.Fatal(err)
}
}
})
}
func BenchmarkPipeline(b *testing.B) {
client := benchmarkRedisClient(10)
defer client.Close()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, err := client.Pipelined(func(pipe redis.Pipeliner) error {
pipe.Set("key", "hello", 0)
pipe.Expire("key", time.Second)
return nil
})
if err != nil {
b.Fatal(err)
}
}
})
}
func BenchmarkZAdd(b *testing.B) {
client := benchmarkRedisClient(10)
defer client.Close()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if err := client.ZAdd("key", redis.Z{float64(1), "hello"}).Err(); err != nil {
b.Fatal(err)
}
}
})
}

573
vendor/github.com/go-redis/redis/cluster.go сгенерированный поставляемый
Просмотреть файл

@@ -1,7 +1,10 @@
package redis
import (
"context"
"errors"
"fmt"
"math"
"math/rand"
"net"
"sync"
@@ -12,10 +15,10 @@ import (
"github.com/go-redis/redis/internal/hashtag"
"github.com/go-redis/redis/internal/pool"
"github.com/go-redis/redis/internal/proto"
"github.com/go-redis/redis/internal/singleflight"
)
var errClusterNoNodes = fmt.Errorf("redis: cluster has no nodes")
var errNilClusterState = fmt.Errorf("redis: cannot load cluster slots")
// ClusterOptions are used to configure a cluster client and should be
// passed to NewClusterClient.
@@ -25,13 +28,15 @@ type ClusterOptions struct {
// The maximum number of retries before giving up. Command is retried
// on network errors and MOVED/ASK redirects.
// Default is 16.
// Default is 8.
MaxRedirects int
// Enables read-only commands on slave nodes.
ReadOnly bool
// Allows routing read-only commands to the closest master or slave node.
RouteByLatency bool
// Allows routing read-only commands to the random master or slave node.
RouteRandomly bool
// Following options are copied from Options struct.
@@ -57,7 +62,7 @@ func (opt *ClusterOptions) init() {
if opt.MaxRedirects == -1 {
opt.MaxRedirects = 0
} else if opt.MaxRedirects == 0 {
opt.MaxRedirects = 16
opt.MaxRedirects = 8
}
if opt.RouteByLatency {
@@ -118,11 +123,11 @@ func (opt *ClusterOptions) clientOptions() *Options {
//------------------------------------------------------------------------------
type clusterNode struct {
Client *Client
Latency time.Duration
Client *Client
loading time.Time
generation uint32
latency uint32 // atomic
generation uint32 // atomic
loading uint32 // atomic
}
func newClusterNode(clOpt *ClusterOptions, addr string) *clusterNode {
@@ -132,36 +137,69 @@ func newClusterNode(clOpt *ClusterOptions, addr string) *clusterNode {
Client: NewClient(opt),
}
node.latency = math.MaxUint32
if clOpt.RouteByLatency {
node.updateLatency()
go node.updateLatency()
}
return &node
}
func (n *clusterNode) Close() error {
return n.Client.Close()
}
func (n *clusterNode) Test() error {
return n.Client.ClusterInfo().Err()
}
func (n *clusterNode) updateLatency() {
const probes = 10
var latency uint32
for i := 0; i < probes; i++ {
start := time.Now()
n.Client.Ping()
n.Latency += time.Since(start)
probe := uint32(time.Since(start) / time.Microsecond)
latency = (latency + probe) / 2
}
n.Latency = n.Latency / probes
atomic.StoreUint32(&n.latency, latency)
}
func (n *clusterNode) Latency() time.Duration {
latency := atomic.LoadUint32(&n.latency)
return time.Duration(latency) * time.Microsecond
}
func (n *clusterNode) MarkAsLoading() {
atomic.StoreUint32(&n.loading, uint32(time.Now().Unix()))
}
func (n *clusterNode) Loading() bool {
return !n.loading.IsZero() && time.Since(n.loading) < time.Minute
const minute = int64(time.Minute / time.Second)
loading := atomic.LoadUint32(&n.loading)
if loading == 0 {
return false
}
if time.Now().Unix()-int64(loading) < minute {
return true
}
atomic.StoreUint32(&n.loading, 0)
return false
}
func (n *clusterNode) Generation() uint32 {
return n.generation
return atomic.LoadUint32(&n.generation)
}
func (n *clusterNode) SetGeneration(gen uint32) {
if gen < n.generation {
panic("gen < n.generation")
for {
v := atomic.LoadUint32(&n.generation)
if gen < v || atomic.CompareAndSwapUint32(&n.generation, v, gen) {
break
}
}
n.generation = gen
}
//------------------------------------------------------------------------------
@@ -169,18 +207,23 @@ func (n *clusterNode) SetGeneration(gen uint32) {
type clusterNodes struct {
opt *ClusterOptions
mu sync.RWMutex
addrs []string
nodes map[string]*clusterNode
closed bool
mu sync.RWMutex
allAddrs []string
allNodes map[string]*clusterNode
clusterAddrs []string
closed bool
nodeCreateGroup singleflight.Group
generation uint32
}
func newClusterNodes(opt *ClusterOptions) *clusterNodes {
return &clusterNodes{
opt: opt,
nodes: make(map[string]*clusterNode),
opt: opt,
allAddrs: opt.Addrs,
allNodes: make(map[string]*clusterNode),
}
}
@@ -194,21 +237,29 @@ func (c *clusterNodes) Close() error {
c.closed = true
var firstErr error
for _, node := range c.nodes {
for _, node := range c.allNodes {
if err := node.Client.Close(); err != nil && firstErr == nil {
firstErr = err
}
}
c.addrs = nil
c.nodes = nil
c.allNodes = nil
c.clusterAddrs = nil
return firstErr
}
func (c *clusterNodes) Addrs() ([]string, error) {
var addrs []string
c.mu.RLock()
closed := c.closed
addrs := c.addrs
if !closed {
if len(c.clusterAddrs) > 0 {
addrs = c.clusterAddrs
} else {
addrs = c.allAddrs
}
}
c.mu.RUnlock()
if closed {
@@ -229,25 +280,66 @@ func (c *clusterNodes) NextGeneration() uint32 {
func (c *clusterNodes) GC(generation uint32) {
var collected []*clusterNode
c.mu.Lock()
for i := 0; i < len(c.addrs); {
addr := c.addrs[i]
node := c.nodes[addr]
for addr, node := range c.allNodes {
if node.Generation() >= generation {
i++
continue
}
c.addrs = append(c.addrs[:i], c.addrs[i+1:]...)
delete(c.nodes, addr)
c.clusterAddrs = remove(c.clusterAddrs, addr)
delete(c.allNodes, addr)
collected = append(collected, node)
}
c.mu.Unlock()
time.AfterFunc(time.Minute, func() {
for _, node := range collected {
_ = node.Client.Close()
}
for _, node := range collected {
_ = node.Client.Close()
}
}
func (c *clusterNodes) GetOrCreate(addr string) (*clusterNode, error) {
var node *clusterNode
var err error
c.mu.RLock()
if c.closed {
err = pool.ErrClosed
} else {
node = c.allNodes[addr]
}
c.mu.RUnlock()
if err != nil {
return nil, err
}
if node != nil {
return node, nil
}
v, err := c.nodeCreateGroup.Do(addr, func() (interface{}, error) {
node := newClusterNode(c.opt, addr)
return node, node.Test()
})
c.mu.Lock()
defer c.mu.Unlock()
if c.closed {
return nil, pool.ErrClosed
}
node, ok := c.allNodes[addr]
if ok {
_ = v.(*clusterNode).Close()
return node, err
}
node = v.(*clusterNode)
c.allAddrs = appendIfNotExists(c.allAddrs, addr)
if err == nil {
c.clusterAddrs = append(c.clusterAddrs, addr)
}
c.allNodes[addr] = node
return node, err
}
func (c *clusterNodes) All() ([]*clusterNode, error) {
@@ -258,42 +350,11 @@ func (c *clusterNodes) All() ([]*clusterNode, error) {
return nil, pool.ErrClosed
}
nodes := make([]*clusterNode, 0, len(c.nodes))
for _, node := range c.nodes {
nodes = append(nodes, node)
cp := make([]*clusterNode, 0, len(c.allNodes))
for _, node := range c.allNodes {
cp = append(cp, node)
}
return nodes, nil
}
func (c *clusterNodes) GetOrCreate(addr string) (*clusterNode, error) {
var node *clusterNode
var ok bool
c.mu.RLock()
if !c.closed {
node, ok = c.nodes[addr]
}
c.mu.RUnlock()
if ok {
return node, nil
}
c.mu.Lock()
defer c.mu.Unlock()
if c.closed {
return nil, pool.ErrClosed
}
node, ok = c.nodes[addr]
if ok {
return node, nil
}
c.addrs = append(c.addrs, addr)
node = newClusterNode(c.opt, addr)
c.nodes[addr] = node
return node, nil
return cp, nil
}
func (c *clusterNodes) Random() (*clusterNode, error) {
@@ -302,20 +363,8 @@ func (c *clusterNodes) Random() (*clusterNode, error) {
return nil, err
}
var nodeErr error
for i := 0; i <= c.opt.MaxRedirects; i++ {
n := rand.Intn(len(addrs))
node, err := c.GetOrCreate(addrs[n])
if err != nil {
return nil, err
}
nodeErr = node.Client.ClusterInfo().Err()
if nodeErr == nil {
return node, nil
}
}
return nil, nodeErr
n := rand.Intn(len(addrs))
return c.GetOrCreate(addrs[n])
}
//------------------------------------------------------------------------------
@@ -367,6 +416,10 @@ func newClusterState(nodes *clusterNodes, slots []ClusterSlot, origin string) (*
}
}
time.AfterFunc(time.Minute, func() {
nodes.GC(c.generation)
})
return &c, nil
}
@@ -416,13 +469,19 @@ func (c *clusterState) slotClosestNode(slot int) (*clusterNode, error) {
if n.Loading() {
continue
}
if node == nil || node.Latency-n.Latency > threshold {
if node == nil || node.Latency()-n.Latency() > threshold {
node = n
}
}
return node, nil
}
func (c *clusterState) slotRandomNode(slot int) *clusterNode {
nodes := c.slotNodes(slot)
n := rand.Intn(len(nodes))
return nodes[n]
}
func (c *clusterState) slotNodes(slot int) []*clusterNode {
if slot >= 0 && slot < len(c.slots) {
return c.slots[slot]
@@ -432,25 +491,83 @@ func (c *clusterState) slotNodes(slot int) []*clusterNode {
//------------------------------------------------------------------------------
type clusterStateHolder struct {
load func() (*clusterState, error)
state atomic.Value
lastErrMu sync.RWMutex
lastErr error
reloading uint32 // atomic
}
func newClusterStateHolder(fn func() (*clusterState, error)) *clusterStateHolder {
return &clusterStateHolder{
load: fn,
}
}
func (c *clusterStateHolder) Load() (*clusterState, error) {
state, err := c.load()
if err != nil {
c.lastErrMu.Lock()
c.lastErr = err
c.lastErrMu.Unlock()
return nil, err
}
c.state.Store(state)
return state, nil
}
func (c *clusterStateHolder) LazyReload() {
if !atomic.CompareAndSwapUint32(&c.reloading, 0, 1) {
return
}
go func() {
defer atomic.StoreUint32(&c.reloading, 0)
_, err := c.Load()
if err == nil {
time.Sleep(time.Second)
}
}()
}
func (c *clusterStateHolder) Get() (*clusterState, error) {
v := c.state.Load()
if v != nil {
return v.(*clusterState), nil
}
c.lastErrMu.RLock()
err := c.lastErr
c.lastErrMu.RUnlock()
if err != nil {
return nil, err
}
return nil, errors.New("redis: cluster has no state")
}
//------------------------------------------------------------------------------
// ClusterClient is a Redis Cluster client representing a pool of zero
// or more underlying connections. It's safe for concurrent use by
// multiple goroutines.
type ClusterClient struct {
cmdable
opt *ClusterOptions
nodes *clusterNodes
_state atomic.Value
ctx context.Context
cmdsInfoOnce internal.Once
cmdsInfo map[string]*CommandInfo
opt *ClusterOptions
nodes *clusterNodes
state *clusterStateHolder
cmdsInfoCache *cmdsInfoCache
process func(Cmder) error
processPipeline func([]Cmder) error
processTxPipeline func([]Cmder) error
// Reports whether slots reloading is in progress.
reloading uint32
}
// NewClusterClient returns a Redis Cluster client as described in
@@ -459,9 +576,11 @@ func NewClusterClient(opt *ClusterOptions) *ClusterClient {
opt.init()
c := &ClusterClient{
opt: opt,
nodes: newClusterNodes(opt),
opt: opt,
nodes: newClusterNodes(opt),
cmdsInfoCache: newCmdsInfoCache(),
}
c.state = newClusterStateHolder(c.loadState)
c.process = c.defaultProcess
c.processPipeline = c.defaultProcessPipeline
@@ -469,20 +588,7 @@ func NewClusterClient(opt *ClusterOptions) *ClusterClient {
c.cmdable.setProcessor(c.Process)
// Add initial nodes.
for _, addr := range opt.Addrs {
_, _ = c.nodes.GetOrCreate(addr)
}
// Preload cluster slots.
for i := 0; i < 10; i++ {
state, err := c.reloadState()
if err == nil {
c._state.Store(state)
break
}
}
_, _ = c.state.Load()
if opt.IdleCheckFrequency > 0 {
go c.reaper(opt.IdleCheckFrequency)
}
@@ -490,6 +596,27 @@ func NewClusterClient(opt *ClusterOptions) *ClusterClient {
return c
}
func (c *ClusterClient) Context() context.Context {
if c.ctx != nil {
return c.ctx
}
return context.Background()
}
func (c *ClusterClient) WithContext(ctx context.Context) *ClusterClient {
if ctx == nil {
panic("nil context")
}
c2 := c.copy()
c2.ctx = ctx
return c2
}
func (c *ClusterClient) copy() *ClusterClient {
cp := *c
return &cp
}
// Options returns read-only Options that were used to create the client.
func (c *ClusterClient) Options() *ClusterOptions {
return c.opt
@@ -499,40 +626,18 @@ func (c *ClusterClient) retryBackoff(attempt int) time.Duration {
return internal.RetryBackoff(attempt, c.opt.MinRetryBackoff, c.opt.MaxRetryBackoff)
}
func (c *ClusterClient) state() (*clusterState, error) {
v := c._state.Load()
if v != nil {
return v.(*clusterState), nil
}
_, err := c.nodes.Addrs()
if err != nil {
return nil, err
}
c.lazyReloadState()
return nil, errNilClusterState
}
func (c *ClusterClient) cmdInfo(name string) *CommandInfo {
err := c.cmdsInfoOnce.Do(func() error {
cmdsInfo, err := c.cmdsInfoCache.Do(func() (map[string]*CommandInfo, error) {
node, err := c.nodes.Random()
if err != nil {
return err
return nil, err
}
cmdsInfo, err := node.Client.Command().Result()
if err != nil {
return err
}
c.cmdsInfo = cmdsInfo
return nil
return node.Client.Command().Result()
})
if err != nil {
return nil
}
info := c.cmdsInfo[name]
info := cmdsInfo[name]
if info == nil {
internal.Logf("info for cmd=%s not found", name)
}
@@ -552,7 +657,12 @@ func (c *ClusterClient) cmdSlot(cmd Cmder) int {
return cmdSlot(cmd, cmdFirstKeyPos(cmd, cmdInfo))
}
func (c *ClusterClient) cmdSlotAndNode(state *clusterState, cmd Cmder) (int, *clusterNode, error) {
func (c *ClusterClient) cmdSlotAndNode(cmd Cmder) (int, *clusterNode, error) {
state, err := c.state.Get()
if err != nil {
return 0, nil, err
}
cmdInfo := c.cmdInfo(cmd.Name())
slot := cmdSlot(cmd, cmdFirstKeyPos(cmd, cmdInfo))
@@ -562,6 +672,11 @@ func (c *ClusterClient) cmdSlotAndNode(state *clusterState, cmd Cmder) (int, *cl
return slot, node, err
}
if c.opt.RouteRandomly {
node := state.slotRandomNode(slot)
return slot, node, nil
}
node, err := state.slotSlaveNode(slot)
return slot, node, err
}
@@ -570,16 +685,24 @@ func (c *ClusterClient) cmdSlotAndNode(state *clusterState, cmd Cmder) (int, *cl
return slot, node, err
}
func (c *ClusterClient) slotMasterNode(slot int) (*clusterNode, error) {
state, err := c.state.Get()
if err != nil {
return nil, err
}
nodes := state.slotNodes(slot)
if len(nodes) > 0 {
return nodes[0], nil
}
return c.nodes.Random()
}
func (c *ClusterClient) Watch(fn func(*Tx) error, keys ...string) error {
if len(keys) == 0 {
return fmt.Errorf("redis: keys don't hash to the same slot")
}
state, err := c.state()
if err != nil {
return err
}
slot := hashtag.Slot(keys[0])
for _, key := range keys[1:] {
if hashtag.Slot(key) != slot {
@@ -587,7 +710,7 @@ func (c *ClusterClient) Watch(fn func(*Tx) error, keys ...string) error {
}
}
node, err := state.slotMasterNode(slot)
node, err := c.slotMasterNode(slot)
if err != nil {
return err
}
@@ -608,7 +731,7 @@ func (c *ClusterClient) Watch(fn func(*Tx) error, keys ...string) error {
moved, ask, addr := internal.IsMovedError(err)
if moved || ask {
c.lazyReloadState()
c.state.LazyReload()
node, err = c.nodes.GetOrCreate(addr)
if err != nil {
return err
@@ -617,10 +740,11 @@ func (c *ClusterClient) Watch(fn func(*Tx) error, keys ...string) error {
}
if err == pool.ErrClosed {
node, err = state.slotMasterNode(slot)
node, err = c.slotMasterNode(slot)
if err != nil {
return err
}
continue
}
return err
@@ -644,31 +768,27 @@ func (c *ClusterClient) WrapProcess(
}
func (c *ClusterClient) Process(cmd Cmder) error {
if c.process != nil {
return c.process(cmd)
}
return c.defaultProcess(cmd)
return c.process(cmd)
}
func (c *ClusterClient) defaultProcess(cmd Cmder) error {
state, err := c.state()
if err != nil {
cmd.setErr(err)
return err
}
_, node, err := c.cmdSlotAndNode(state, cmd)
if err != nil {
cmd.setErr(err)
return err
}
var node *clusterNode
var ask bool
for attempt := 0; attempt <= c.opt.MaxRedirects; attempt++ {
if attempt > 0 {
time.Sleep(c.retryBackoff(attempt))
}
if node == nil {
var err error
_, node, err = c.cmdSlotAndNode(cmd)
if err != nil {
cmd.setErr(err)
break
}
}
var err error
if ask {
pipe := node.Client.Pipeline()
_ = pipe.Process(NewCmd("ASKING"))
@@ -687,15 +807,13 @@ func (c *ClusterClient) defaultProcess(cmd Cmder) error {
// If slave is loading - read from master.
if c.opt.ReadOnly && internal.IsLoadingError(err) {
// TODO: race
node.loading = time.Now()
node.MarkAsLoading()
continue
}
if internal.IsRetryableError(err, true) {
var nodeErr error
node, nodeErr = c.nodes.Random()
if nodeErr != nil {
node, err = c.nodes.Random()
if err != nil {
break
}
continue
@@ -705,22 +823,18 @@ func (c *ClusterClient) defaultProcess(cmd Cmder) error {
var addr string
moved, ask, addr = internal.IsMovedError(err)
if moved || ask {
c.lazyReloadState()
c.state.LazyReload()
var nodeErr error
node, nodeErr = c.nodes.GetOrCreate(addr)
if nodeErr != nil {
node, err = c.nodes.GetOrCreate(addr)
if err != nil {
break
}
continue
}
if err == pool.ErrClosed {
_, node, err = c.cmdSlotAndNode(state, cmd)
if err != nil {
cmd.setErr(err)
return err
}
node = nil
continue
}
break
@@ -732,7 +846,7 @@ func (c *ClusterClient) defaultProcess(cmd Cmder) error {
// ForEachMaster concurrently calls the fn on each master node in the cluster.
// It returns the first error if any.
func (c *ClusterClient) ForEachMaster(fn func(client *Client) error) error {
state, err := c.state()
state, err := c.state.Get()
if err != nil {
return err
}
@@ -765,7 +879,7 @@ func (c *ClusterClient) ForEachMaster(fn func(client *Client) error) error {
// ForEachSlave concurrently calls the fn on each slave node in the cluster.
// It returns the first error if any.
func (c *ClusterClient) ForEachSlave(fn func(client *Client) error) error {
state, err := c.state()
state, err := c.state.Get()
if err != nil {
return err
}
@@ -798,7 +912,7 @@ func (c *ClusterClient) ForEachSlave(fn func(client *Client) error) error {
// ForEachNode concurrently calls the fn on each known node in the cluster.
// It returns the first error if any.
func (c *ClusterClient) ForEachNode(fn func(client *Client) error) error {
state, err := c.state()
state, err := c.state.Get()
if err != nil {
return err
}
@@ -838,7 +952,7 @@ func (c *ClusterClient) ForEachNode(fn func(client *Client) error) error {
func (c *ClusterClient) PoolStats() *PoolStats {
var acc PoolStats
state, _ := c.state()
state, _ := c.state.Get()
if state == nil {
return &acc
}
@@ -868,46 +982,34 @@ func (c *ClusterClient) PoolStats() *PoolStats {
return &acc
}
func (c *ClusterClient) lazyReloadState() {
if !atomic.CompareAndSwapUint32(&c.reloading, 0, 1) {
return
func (c *ClusterClient) loadState() (*clusterState, error) {
addrs, err := c.nodes.Addrs()
if err != nil {
return nil, err
}
go func() {
defer atomic.StoreUint32(&c.reloading, 0)
for {
state, err := c.reloadState()
if err == pool.ErrClosed {
return
var firstErr error
for _, addr := range addrs {
node, err := c.nodes.GetOrCreate(addr)
if err != nil {
if firstErr == nil {
firstErr = err
}
if err != nil {
time.Sleep(time.Millisecond)
continue
}
c._state.Store(state)
time.Sleep(5 * time.Second)
c.nodes.GC(state.generation)
break
continue
}
}()
}
// Not thread-safe.
func (c *ClusterClient) reloadState() (*clusterState, error) {
node, err := c.nodes.Random()
if err != nil {
return nil, err
slots, err := node.Client.ClusterSlots().Result()
if err != nil {
if firstErr == nil {
firstErr = err
}
continue
}
return newClusterState(c.nodes, slots, node.Client.opt.Addr)
}
slots, err := node.Client.ClusterSlots().Result()
if err != nil {
return nil, err
}
return newClusterState(c.nodes, slots, node.Client.opt.Addr)
return nil, firstErr
}
// reaper closes idle connections to the cluster.
@@ -991,7 +1093,7 @@ func (c *ClusterClient) defaultProcessPipeline(cmds []Cmder) error {
}
func (c *ClusterClient) mapCmdsByNode(cmds []Cmder) (map[*clusterNode][]Cmder, error) {
state, err := c.state()
state, err := c.state.Get()
if err != nil {
setCmdsErr(cmds, err)
return nil, err
@@ -1024,15 +1126,17 @@ func (c *ClusterClient) remapCmds(cmds []Cmder, failedCmds map[*clusterNode][]Cm
func (c *ClusterClient) pipelineProcessCmds(
node *clusterNode, cn *pool.Conn, cmds []Cmder, failedCmds map[*clusterNode][]Cmder,
) error {
cn.SetWriteTimeout(c.opt.WriteTimeout)
if err := writeCmd(cn, cmds...); err != nil {
_ = cn.SetWriteTimeout(c.opt.WriteTimeout)
err := writeCmd(cn, cmds...)
if err != nil {
setCmdsErr(cmds, err)
failedCmds[node] = cmds
return err
}
// Set read timeout for all commands.
cn.SetReadTimeout(c.opt.ReadTimeout)
_ = cn.SetReadTimeout(c.opt.ReadTimeout)
return c.pipelineReadCmds(cn, cmds, failedCmds)
}
@@ -1065,7 +1169,7 @@ func (c *ClusterClient) checkMovedErr(
moved, ask, addr := internal.IsMovedError(err)
if moved {
c.lazyReloadState()
c.state.LazyReload()
node, err := c.nodes.GetOrCreate(addr)
if err != nil {
@@ -1103,7 +1207,7 @@ func (c *ClusterClient) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
}
func (c *ClusterClient) defaultProcessTxPipeline(cmds []Cmder) error {
state, err := c.state()
state, err := c.state.Get()
if err != nil {
return err
}
@@ -1249,12 +1353,7 @@ func (c *ClusterClient) pubSub(channels []string) *PubSub {
slot = -1
}
state, err := c.state()
if err != nil {
return nil, err
}
masterNode, err := state.slotMasterNode(slot)
masterNode, err := c.slotMasterNode(slot)
if err != nil {
return nil, err
}
@@ -1310,3 +1409,31 @@ func appendNode(nodes []*clusterNode, node *clusterNode) []*clusterNode {
}
return append(nodes, node)
}
func appendIfNotExists(ss []string, es ...string) []string {
loop:
for _, e := range es {
for _, s := range ss {
if s == e {
continue loop
}
}
ss = append(ss, e)
}
return ss
}
func remove(ss []string, es ...string) []string {
if len(es) == 0 {
return ss[:0]
}
for _, e := range es {
for i, s := range ss {
if s == e {
ss = append(ss[:i], ss[i+1:]...)
break
}
}
}
return ss
}

813
vendor/github.com/go-redis/redis/cluster_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,813 +0,0 @@
package redis_test
import (
"bytes"
"fmt"
"net"
"strconv"
"strings"
"sync"
"testing"
"time"
"github.com/go-redis/redis"
"github.com/go-redis/redis/internal/hashtag"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
type clusterScenario struct {
ports []string
nodeIds []string
processes map[string]*redisProcess
clients map[string]*redis.Client
}
func (s *clusterScenario) masters() []*redis.Client {
result := make([]*redis.Client, 3)
for pos, port := range s.ports[:3] {
result[pos] = s.clients[port]
}
return result
}
func (s *clusterScenario) slaves() []*redis.Client {
result := make([]*redis.Client, 3)
for pos, port := range s.ports[3:] {
result[pos] = s.clients[port]
}
return result
}
func (s *clusterScenario) addrs() []string {
addrs := make([]string, len(s.ports))
for i, port := range s.ports {
addrs[i] = net.JoinHostPort("127.0.0.1", port)
}
return addrs
}
func (s *clusterScenario) clusterClient(opt *redis.ClusterOptions) *redis.ClusterClient {
opt.Addrs = s.addrs()
return redis.NewClusterClient(opt)
}
func startCluster(scenario *clusterScenario) error {
// Start processes and collect node ids
for pos, port := range scenario.ports {
process, err := startRedis(port, "--cluster-enabled", "yes")
if err != nil {
return err
}
client := redis.NewClient(&redis.Options{
Addr: ":" + port,
})
info, err := client.ClusterNodes().Result()
if err != nil {
return err
}
scenario.processes[port] = process
scenario.clients[port] = client
scenario.nodeIds[pos] = info[:40]
}
// Meet cluster nodes.
for _, client := range scenario.clients {
err := client.ClusterMeet("127.0.0.1", scenario.ports[0]).Err()
if err != nil {
return err
}
}
// Bootstrap masters.
slots := []int{0, 5000, 10000, 16384}
for pos, master := range scenario.masters() {
err := master.ClusterAddSlotsRange(slots[pos], slots[pos+1]-1).Err()
if err != nil {
return err
}
}
// Bootstrap slaves.
for idx, slave := range scenario.slaves() {
masterId := scenario.nodeIds[idx]
// Wait until master is available
err := eventually(func() error {
s := slave.ClusterNodes().Val()
wanted := masterId
if !strings.Contains(s, wanted) {
return fmt.Errorf("%q does not contain %q", s, wanted)
}
return nil
}, 10*time.Second)
if err != nil {
return err
}
err = slave.ClusterReplicate(masterId).Err()
if err != nil {
return err
}
}
// Wait until all nodes have consistent info.
for _, client := range scenario.clients {
err := eventually(func() error {
res, err := client.ClusterSlots().Result()
if err != nil {
return err
}
wanted := []redis.ClusterSlot{
{0, 4999, []redis.ClusterNode{{"", "127.0.0.1:8220"}, {"", "127.0.0.1:8223"}}},
{5000, 9999, []redis.ClusterNode{{"", "127.0.0.1:8221"}, {"", "127.0.0.1:8224"}}},
{10000, 16383, []redis.ClusterNode{{"", "127.0.0.1:8222"}, {"", "127.0.0.1:8225"}}},
}
return assertSlotsEqual(res, wanted)
}, 30*time.Second)
if err != nil {
return err
}
}
return nil
}
func assertSlotsEqual(slots, wanted []redis.ClusterSlot) error {
outer_loop:
for _, s2 := range wanted {
for _, s1 := range slots {
if slotEqual(s1, s2) {
continue outer_loop
}
}
return fmt.Errorf("%v not found in %v", s2, slots)
}
return nil
}
func slotEqual(s1, s2 redis.ClusterSlot) bool {
if s1.Start != s2.Start {
return false
}
if s1.End != s2.End {
return false
}
if len(s1.Nodes) != len(s2.Nodes) {
return false
}
for i, n1 := range s1.Nodes {
if n1.Addr != s2.Nodes[i].Addr {
return false
}
}
return true
}
func stopCluster(scenario *clusterScenario) error {
for _, client := range scenario.clients {
if err := client.Close(); err != nil {
return err
}
}
for _, process := range scenario.processes {
if err := process.Close(); err != nil {
return err
}
}
return nil
}
//------------------------------------------------------------------------------
var _ = Describe("ClusterClient", func() {
var opt *redis.ClusterOptions
var client *redis.ClusterClient
assertClusterClient := func() {
It("should GET/SET/DEL", func() {
val, err := client.Get("A").Result()
Expect(err).To(Equal(redis.Nil))
Expect(val).To(Equal(""))
val, err = client.Set("A", "VALUE", 0).Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).To(Equal("OK"))
Eventually(func() string {
return client.Get("A").Val()
}, 30*time.Second).Should(Equal("VALUE"))
cnt, err := client.Del("A").Result()
Expect(err).NotTo(HaveOccurred())
Expect(cnt).To(Equal(int64(1)))
})
It("follows redirects", func() {
Expect(client.Set("A", "VALUE", 0).Err()).NotTo(HaveOccurred())
slot := hashtag.Slot("A")
client.SwapSlotNodes(slot)
Eventually(func() string {
return client.Get("A").Val()
}, 30*time.Second).Should(Equal("VALUE"))
})
It("distributes keys", func() {
for i := 0; i < 100; i++ {
err := client.Set(fmt.Sprintf("key%d", i), "value", 0).Err()
Expect(err).NotTo(HaveOccurred())
}
for _, master := range cluster.masters() {
Eventually(func() string {
return master.Info("keyspace").Val()
}, 30*time.Second).Should(Or(
ContainSubstring("keys=31"),
ContainSubstring("keys=29"),
ContainSubstring("keys=40"),
))
}
})
It("distributes keys when using EVAL", func() {
script := redis.NewScript(`
local r = redis.call('SET', KEYS[1], ARGV[1])
return r
`)
var key string
for i := 0; i < 100; i++ {
key = fmt.Sprintf("key%d", i)
err := script.Run(client, []string{key}, "value").Err()
Expect(err).NotTo(HaveOccurred())
}
for _, master := range cluster.masters() {
Eventually(func() string {
return master.Info("keyspace").Val()
}, 30*time.Second).Should(Or(
ContainSubstring("keys=31"),
ContainSubstring("keys=29"),
ContainSubstring("keys=40"),
))
}
})
It("supports Watch", func() {
var incr func(string) error
// Transactionally increments key using GET and SET commands.
incr = func(key string) error {
err := client.Watch(func(tx *redis.Tx) error {
n, err := tx.Get(key).Int64()
if err != nil && err != redis.Nil {
return err
}
_, err = tx.Pipelined(func(pipe redis.Pipeliner) error {
pipe.Set(key, strconv.FormatInt(n+1, 10), 0)
return nil
})
return err
}, key)
if err == redis.TxFailedErr {
return incr(key)
}
return err
}
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer GinkgoRecover()
defer wg.Done()
err := incr("key")
Expect(err).NotTo(HaveOccurred())
}()
}
wg.Wait()
n, err := client.Get("key").Int64()
Expect(err).NotTo(HaveOccurred())
Expect(n).To(Equal(int64(100)))
})
Describe("pipelining", func() {
var pipe *redis.Pipeline
assertPipeline := func() {
keys := []string{"A", "B", "C", "D", "E", "F", "G"}
It("follows redirects", func() {
for _, key := range keys {
slot := hashtag.Slot(key)
client.SwapSlotNodes(slot)
}
for i, key := range keys {
pipe.Set(key, key+"_value", 0)
pipe.Expire(key, time.Duration(i+1)*time.Hour)
}
cmds, err := pipe.Exec()
Expect(err).NotTo(HaveOccurred())
Expect(cmds).To(HaveLen(14))
for _, key := range keys {
slot := hashtag.Slot(key)
client.SwapSlotNodes(slot)
}
for _, key := range keys {
pipe.Get(key)
pipe.TTL(key)
}
cmds, err = pipe.Exec()
Expect(err).NotTo(HaveOccurred())
Expect(cmds).To(HaveLen(14))
for i, key := range keys {
get := cmds[i*2].(*redis.StringCmd)
Expect(get.Val()).To(Equal(key + "_value"))
ttl := cmds[(i*2)+1].(*redis.DurationCmd)
dur := time.Duration(i+1) * time.Hour
Expect(ttl.Val()).To(BeNumerically("~", dur, 5*time.Second))
}
})
It("works with missing keys", func() {
pipe.Set("A", "A_value", 0)
pipe.Set("C", "C_value", 0)
_, err := pipe.Exec()
Expect(err).NotTo(HaveOccurred())
a := pipe.Get("A")
b := pipe.Get("B")
c := pipe.Get("C")
cmds, err := pipe.Exec()
Expect(err).To(Equal(redis.Nil))
Expect(cmds).To(HaveLen(3))
Expect(a.Err()).NotTo(HaveOccurred())
Expect(a.Val()).To(Equal("A_value"))
Expect(b.Err()).To(Equal(redis.Nil))
Expect(b.Val()).To(Equal(""))
Expect(c.Err()).NotTo(HaveOccurred())
Expect(c.Val()).To(Equal("C_value"))
})
}
Describe("with Pipeline", func() {
BeforeEach(func() {
pipe = client.Pipeline().(*redis.Pipeline)
})
AfterEach(func() {
Expect(pipe.Close()).NotTo(HaveOccurred())
})
assertPipeline()
})
Describe("with TxPipeline", func() {
BeforeEach(func() {
pipe = client.TxPipeline().(*redis.Pipeline)
})
AfterEach(func() {
Expect(pipe.Close()).NotTo(HaveOccurred())
})
assertPipeline()
})
})
It("supports PubSub", func() {
pubsub := client.Subscribe("mychannel")
defer pubsub.Close()
Eventually(func() error {
_, err := client.Publish("mychannel", "hello").Result()
if err != nil {
return err
}
msg, err := pubsub.ReceiveTimeout(time.Second)
if err != nil {
return err
}
_, ok := msg.(*redis.Message)
if !ok {
return fmt.Errorf("got %T, wanted *redis.Message", msg)
}
return nil
}, 30*time.Second).ShouldNot(HaveOccurred())
})
}
Describe("ClusterClient", func() {
BeforeEach(func() {
opt = redisClusterOptions()
client = cluster.clusterClient(opt)
_ = client.ForEachMaster(func(master *redis.Client) error {
return master.FlushDB().Err()
})
})
AfterEach(func() {
_ = client.ForEachMaster(func(master *redis.Client) error {
return master.FlushDB().Err()
})
Expect(client.Close()).NotTo(HaveOccurred())
})
It("returns pool stats", func() {
Expect(client.PoolStats()).To(BeAssignableToTypeOf(&redis.PoolStats{}))
})
It("removes idle connections", func() {
stats := client.PoolStats()
Expect(stats.TotalConns).NotTo(BeZero())
Expect(stats.FreeConns).NotTo(BeZero())
time.Sleep(2 * time.Second)
stats = client.PoolStats()
Expect(stats.TotalConns).To(BeZero())
Expect(stats.FreeConns).To(BeZero())
})
It("returns an error when there are no attempts left", func() {
opt := redisClusterOptions()
opt.MaxRedirects = -1
client := cluster.clusterClient(opt)
slot := hashtag.Slot("A")
client.SwapSlotNodes(slot)
err := client.Get("A").Err()
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("MOVED"))
Expect(client.Close()).NotTo(HaveOccurred())
})
It("calls fn for every master node", func() {
for i := 0; i < 10; i++ {
Expect(client.Set(strconv.Itoa(i), "", 0).Err()).NotTo(HaveOccurred())
}
err := client.ForEachMaster(func(master *redis.Client) error {
return master.FlushDB().Err()
})
Expect(err).NotTo(HaveOccurred())
size, err := client.DBSize().Result()
Expect(err).NotTo(HaveOccurred())
Expect(size).To(Equal(int64(0)))
})
It("should CLUSTER SLOTS", func() {
res, err := client.ClusterSlots().Result()
Expect(err).NotTo(HaveOccurred())
Expect(res).To(HaveLen(3))
wanted := []redis.ClusterSlot{
{0, 4999, []redis.ClusterNode{{"", "127.0.0.1:8220"}, {"", "127.0.0.1:8223"}}},
{5000, 9999, []redis.ClusterNode{{"", "127.0.0.1:8221"}, {"", "127.0.0.1:8224"}}},
{10000, 16383, []redis.ClusterNode{{"", "127.0.0.1:8222"}, {"", "127.0.0.1:8225"}}},
}
Expect(assertSlotsEqual(res, wanted)).NotTo(HaveOccurred())
})
It("should CLUSTER NODES", func() {
res, err := client.ClusterNodes().Result()
Expect(err).NotTo(HaveOccurred())
Expect(len(res)).To(BeNumerically(">", 400))
})
It("should CLUSTER INFO", func() {
res, err := client.ClusterInfo().Result()
Expect(err).NotTo(HaveOccurred())
Expect(res).To(ContainSubstring("cluster_known_nodes:6"))
})
It("should CLUSTER KEYSLOT", func() {
hashSlot, err := client.ClusterKeySlot("somekey").Result()
Expect(err).NotTo(HaveOccurred())
Expect(hashSlot).To(Equal(int64(hashtag.Slot("somekey"))))
})
It("should CLUSTER COUNT-FAILURE-REPORTS", func() {
n, err := client.ClusterCountFailureReports(cluster.nodeIds[0]).Result()
Expect(err).NotTo(HaveOccurred())
Expect(n).To(Equal(int64(0)))
})
It("should CLUSTER COUNTKEYSINSLOT", func() {
n, err := client.ClusterCountKeysInSlot(10).Result()
Expect(err).NotTo(HaveOccurred())
Expect(n).To(Equal(int64(0)))
})
It("should CLUSTER SAVECONFIG", func() {
res, err := client.ClusterSaveConfig().Result()
Expect(err).NotTo(HaveOccurred())
Expect(res).To(Equal("OK"))
})
It("should CLUSTER SLAVES", func() {
nodesList, err := client.ClusterSlaves(cluster.nodeIds[0]).Result()
Expect(err).NotTo(HaveOccurred())
Expect(nodesList).Should(ContainElement(ContainSubstring("slave")))
Expect(nodesList).Should(HaveLen(1))
})
It("should RANDOMKEY", func() {
const nkeys = 100
for i := 0; i < nkeys; i++ {
err := client.Set(fmt.Sprintf("key%d", i), "value", 0).Err()
Expect(err).NotTo(HaveOccurred())
}
var keys []string
addKey := func(key string) {
for _, k := range keys {
if k == key {
return
}
}
keys = append(keys, key)
}
for i := 0; i < nkeys*10; i++ {
key := client.RandomKey().Val()
addKey(key)
}
Expect(len(keys)).To(BeNumerically("~", nkeys, nkeys/10))
})
assertClusterClient()
})
Describe("ClusterClient failover", func() {
BeforeEach(func() {
opt = redisClusterOptions()
client = cluster.clusterClient(opt)
_ = client.ForEachMaster(func(master *redis.Client) error {
return master.FlushDB().Err()
})
_ = client.ForEachSlave(func(slave *redis.Client) error {
Eventually(func() int64 {
return client.DBSize().Val()
}, 30*time.Second).Should(Equal(int64(0)))
return slave.ClusterFailover().Err()
})
})
AfterEach(func() {
_ = client.ForEachMaster(func(master *redis.Client) error {
return master.FlushDB().Err()
})
Expect(client.Close()).NotTo(HaveOccurred())
})
assertClusterClient()
})
Describe("ClusterClient with RouteByLatency", func() {
BeforeEach(func() {
opt = redisClusterOptions()
opt.RouteByLatency = true
client = cluster.clusterClient(opt)
_ = client.ForEachMaster(func(master *redis.Client) error {
return master.FlushDB().Err()
})
_ = client.ForEachSlave(func(slave *redis.Client) error {
Eventually(func() int64 {
return client.DBSize().Val()
}, 30*time.Second).Should(Equal(int64(0)))
return nil
})
})
AfterEach(func() {
_ = client.ForEachMaster(func(master *redis.Client) error {
return master.FlushDB().Err()
})
Expect(client.Close()).NotTo(HaveOccurred())
})
assertClusterClient()
})
})
var _ = Describe("ClusterClient without nodes", func() {
var client *redis.ClusterClient
BeforeEach(func() {
client = redis.NewClusterClient(&redis.ClusterOptions{})
})
AfterEach(func() {
Expect(client.Close()).NotTo(HaveOccurred())
})
It("Ping returns an error", func() {
err := client.Ping().Err()
Expect(err).To(MatchError("redis: cluster has no nodes"))
})
It("pipeline returns an error", func() {
_, err := client.Pipelined(func(pipe redis.Pipeliner) error {
pipe.Ping()
return nil
})
Expect(err).To(MatchError("redis: cluster has no nodes"))
})
})
var _ = Describe("ClusterClient without valid nodes", func() {
var client *redis.ClusterClient
BeforeEach(func() {
client = redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{redisAddr},
})
})
AfterEach(func() {
Expect(client.Close()).NotTo(HaveOccurred())
})
It("returns an error", func() {
err := client.Ping().Err()
Expect(err).To(MatchError("redis: cannot load cluster slots"))
})
It("pipeline returns an error", func() {
_, err := client.Pipelined(func(pipe redis.Pipeliner) error {
pipe.Ping()
return nil
})
Expect(err).To(MatchError("redis: cannot load cluster slots"))
})
})
var _ = Describe("ClusterClient timeout", func() {
var client *redis.ClusterClient
AfterEach(func() {
_ = client.Close()
})
testTimeout := func() {
It("Ping timeouts", func() {
err := client.Ping().Err()
Expect(err).To(HaveOccurred())
Expect(err.(net.Error).Timeout()).To(BeTrue())
})
It("Pipeline timeouts", func() {
_, err := client.Pipelined(func(pipe redis.Pipeliner) error {
pipe.Ping()
return nil
})
Expect(err).To(HaveOccurred())
Expect(err.(net.Error).Timeout()).To(BeTrue())
})
It("Tx timeouts", func() {
err := client.Watch(func(tx *redis.Tx) error {
return tx.Ping().Err()
}, "foo")
Expect(err).To(HaveOccurred())
Expect(err.(net.Error).Timeout()).To(BeTrue())
})
It("Tx Pipeline timeouts", func() {
err := client.Watch(func(tx *redis.Tx) error {
_, err := tx.Pipelined(func(pipe redis.Pipeliner) error {
pipe.Ping()
return nil
})
return err
}, "foo")
Expect(err).To(HaveOccurred())
Expect(err.(net.Error).Timeout()).To(BeTrue())
})
}
const pause = time.Second
Context("read/write timeout", func() {
BeforeEach(func() {
opt := redisClusterOptions()
opt.ReadTimeout = 100 * time.Millisecond
opt.WriteTimeout = 100 * time.Millisecond
opt.MaxRedirects = 1
client = cluster.clusterClient(opt)
err := client.ForEachNode(func(client *redis.Client) error {
return client.ClientPause(pause).Err()
})
Expect(err).NotTo(HaveOccurred())
})
AfterEach(func() {
client.ForEachNode(func(client *redis.Client) error {
Eventually(func() error {
return client.Ping().Err()
}, 2*pause).ShouldNot(HaveOccurred())
return nil
})
})
testTimeout()
})
})
//------------------------------------------------------------------------------
func BenchmarkRedisClusterPing(b *testing.B) {
if testing.Short() {
b.Skip("skipping in short mode")
}
cluster := &clusterScenario{
ports: []string{"8220", "8221", "8222", "8223", "8224", "8225"},
nodeIds: make([]string, 6),
processes: make(map[string]*redisProcess, 6),
clients: make(map[string]*redis.Client, 6),
}
if err := startCluster(cluster); err != nil {
b.Fatal(err)
}
defer stopCluster(cluster)
client := cluster.clusterClient(redisClusterOptions())
defer client.Close()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if err := client.Ping().Err(); err != nil {
b.Fatal(err)
}
}
})
}
func BenchmarkRedisClusterSetString(b *testing.B) {
if testing.Short() {
b.Skip("skipping in short mode")
}
cluster := &clusterScenario{
ports: []string{"8220", "8221", "8222", "8223", "8224", "8225"},
nodeIds: make([]string, 6),
processes: make(map[string]*redisProcess, 6),
clients: make(map[string]*redis.Client, 6),
}
if err := startCluster(cluster); err != nil {
b.Fatal(err)
}
defer stopCluster(cluster)
client := cluster.clusterClient(redisClusterOptions())
defer client.Close()
value := string(bytes.Repeat([]byte{'1'}, 10000))
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if err := client.Set("key", value, 0).Err(); err != nil {
b.Fatal(err)
}
}
})
}

26
vendor/github.com/go-redis/redis/command.go сгенерированный поставляемый
Просмотреть файл

@@ -10,6 +10,7 @@ import (
"github.com/go-redis/redis/internal"
"github.com/go-redis/redis/internal/pool"
"github.com/go-redis/redis/internal/proto"
"github.com/go-redis/redis/internal/util"
)
type Cmder interface {
@@ -436,7 +437,7 @@ func NewStringCmd(args ...interface{}) *StringCmd {
}
func (cmd *StringCmd) Val() string {
return internal.BytesToString(cmd.val)
return util.BytesToString(cmd.val)
}
func (cmd *StringCmd) Result() (string, error) {
@@ -1022,3 +1023,26 @@ func (cmd *CommandsInfoCmd) readReply(cn *pool.Conn) error {
cmd.val = v.(map[string]*CommandInfo)
return nil
}
//------------------------------------------------------------------------------
type cmdsInfoCache struct {
once internal.Once
cmds map[string]*CommandInfo
}
func newCmdsInfoCache() *cmdsInfoCache {
return &cmdsInfoCache{}
}
func (c *cmdsInfoCache) Do(fn func() (map[string]*CommandInfo, error)) (map[string]*CommandInfo, error) {
err := c.once.Do(func() error {
cmds, err := fn()
if err != nil {
return err
}
c.cmds = cmds
return nil
})
return c.cmds, err
}

60
vendor/github.com/go-redis/redis/command_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,60 +0,0 @@
package redis_test
import (
"github.com/go-redis/redis"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Cmd", func() {
var client *redis.Client
BeforeEach(func() {
client = redis.NewClient(redisOptions())
Expect(client.FlushDB().Err()).NotTo(HaveOccurred())
})
AfterEach(func() {
Expect(client.Close()).NotTo(HaveOccurred())
})
It("implements Stringer", func() {
set := client.Set("foo", "bar", 0)
Expect(set.String()).To(Equal("set foo bar: OK"))
get := client.Get("foo")
Expect(get.String()).To(Equal("get foo: bar"))
})
It("has val/err", func() {
set := client.Set("key", "hello", 0)
Expect(set.Err()).NotTo(HaveOccurred())
Expect(set.Val()).To(Equal("OK"))
get := client.Get("key")
Expect(get.Err()).NotTo(HaveOccurred())
Expect(get.Val()).To(Equal("hello"))
Expect(set.Err()).NotTo(HaveOccurred())
Expect(set.Val()).To(Equal("OK"))
})
It("has helpers", func() {
set := client.Set("key", "10", 0)
Expect(set.Err()).NotTo(HaveOccurred())
n, err := client.Get("key").Int64()
Expect(err).NotTo(HaveOccurred())
Expect(n).To(Equal(int64(10)))
un, err := client.Get("key").Uint64()
Expect(err).NotTo(HaveOccurred())
Expect(un).To(Equal(uint64(10)))
f, err := client.Get("key").Float64()
Expect(err).NotTo(HaveOccurred())
Expect(f).To(Equal(float64(10)))
})
})

146
vendor/github.com/go-redis/redis/commands.go сгенерированный поставляемый
Просмотреть файл

@@ -1,6 +1,7 @@
package redis
import (
"errors"
"io"
"time"
@@ -38,6 +39,22 @@ func formatSec(dur time.Duration) int64 {
return int64(dur / time.Second)
}
func appendArgs(dst, src []interface{}) []interface{} {
if len(src) == 1 {
if ss, ok := src[0].([]string); ok {
for _, s := range ss {
dst = append(dst, s)
}
return dst
}
}
for _, v := range src {
dst = append(dst, v)
}
return dst
}
type Cmdable interface {
Pipeline() Pipeliner
Pipelined(fn func(Pipeliner) error) ([]Cmder, error)
@@ -70,8 +87,10 @@ type Cmdable interface {
RenameNX(key, newkey string) *BoolCmd
Restore(key string, ttl time.Duration, value string) *StatusCmd
RestoreReplace(key string, ttl time.Duration, value string) *StatusCmd
Sort(key string, sort Sort) *StringSliceCmd
SortInterfaces(key string, sort Sort) *SliceCmd
Sort(key string, sort *Sort) *StringSliceCmd
SortStore(key, store string, sort *Sort) *IntCmd
SortInterfaces(key string, sort *Sort) *SliceCmd
Touch(keys ...string) *IntCmd
TTL(key string) *DurationCmd
Type(key string) *StatusCmd
Scan(cursor uint64, match string, count int64) *ScanCmd
@@ -195,6 +214,7 @@ type Cmdable interface {
ConfigGet(parameter string) *SliceCmd
ConfigResetStat() *StatusCmd
ConfigSet(parameter, value string) *StatusCmd
ConfigRewrite() *StatusCmd
DBSize() *IntCmd
FlushAll() *StatusCmd
FlushAllAsync() *StatusCmd
@@ -210,7 +230,7 @@ type Cmdable interface {
Time() *TimeCmd
Eval(script string, keys []string, args ...interface{}) *Cmd
EvalSha(sha1 string, keys []string, args ...interface{}) *Cmd
ScriptExists(scripts ...string) *BoolSliceCmd
ScriptExists(hashes ...string) *BoolSliceCmd
ScriptFlush() *StatusCmd
ScriptKill() *StatusCmd
ScriptLoad(script string) *StringCmd
@@ -252,6 +272,7 @@ type StatefulCmdable interface {
Cmdable
Auth(password string) *StatusCmd
Select(index int) *StatusCmd
SwapDB(index1, index2 int) *StatusCmd
ClientSetName(name string) *BoolCmd
ReadOnly() *StatusCmd
ReadWrite() *StatusCmd
@@ -316,6 +337,12 @@ func (c *statefulCmdable) Select(index int) *StatusCmd {
return cmd
}
func (c *statefulCmdable) SwapDB(index1, index2 int) *StatusCmd {
cmd := NewStatusCmd("swapdb", index1, index2)
c.process(cmd)
return cmd
}
//------------------------------------------------------------------------------
func (c *cmdable) Del(keys ...string) *IntCmd {
@@ -484,11 +511,10 @@ func (c *cmdable) RestoreReplace(key string, ttl time.Duration, value string) *S
type Sort struct {
By string
Offset, Count float64
Offset, Count int64
Get []string
Order string
IsAlpha bool
Store string
Alpha bool
}
func (sort *Sort) args(key string) []interface{} {
@@ -505,27 +531,45 @@ func (sort *Sort) args(key string) []interface{} {
if sort.Order != "" {
args = append(args, sort.Order)
}
if sort.IsAlpha {
if sort.Alpha {
args = append(args, "alpha")
}
if sort.Store != "" {
args = append(args, "store", sort.Store)
}
return args
}
func (c *cmdable) Sort(key string, sort Sort) *StringSliceCmd {
func (c *cmdable) Sort(key string, sort *Sort) *StringSliceCmd {
cmd := NewStringSliceCmd(sort.args(key)...)
c.process(cmd)
return cmd
}
func (c *cmdable) SortInterfaces(key string, sort Sort) *SliceCmd {
func (c *cmdable) SortStore(key, store string, sort *Sort) *IntCmd {
args := sort.args(key)
if store != "" {
args = append(args, "store", store)
}
cmd := NewIntCmd(args...)
c.process(cmd)
return cmd
}
func (c *cmdable) SortInterfaces(key string, sort *Sort) *SliceCmd {
cmd := NewSliceCmd(sort.args(key)...)
c.process(cmd)
return cmd
}
func (c *cmdable) Touch(keys ...string) *IntCmd {
args := make([]interface{}, len(keys)+1)
args[0] = "touch"
for i, key := range keys {
args[i+1] = key
}
cmd := NewIntCmd(args...)
c.process(cmd)
return cmd
}
func (c *cmdable) TTL(key string) *DurationCmd {
cmd := NewDurationCmd(time.Second, "ttl", key)
c.process(cmd)
@@ -732,22 +776,18 @@ func (c *cmdable) MGet(keys ...string) *SliceCmd {
}
func (c *cmdable) MSet(pairs ...interface{}) *StatusCmd {
args := make([]interface{}, 1+len(pairs))
args := make([]interface{}, 1, 1+len(pairs))
args[0] = "mset"
for i, pair := range pairs {
args[1+i] = pair
}
args = appendArgs(args, pairs)
cmd := NewStatusCmd(args...)
c.process(cmd)
return cmd
}
func (c *cmdable) MSetNX(pairs ...interface{}) *BoolCmd {
args := make([]interface{}, 1+len(pairs))
args := make([]interface{}, 1, 1+len(pairs))
args[0] = "msetnx"
for i, pair := range pairs {
args[1+i] = pair
}
args = appendArgs(args, pairs)
cmd := NewBoolCmd(args...)
c.process(cmd)
return cmd
@@ -1012,12 +1052,10 @@ func (c *cmdable) LPop(key string) *StringCmd {
}
func (c *cmdable) LPush(key string, values ...interface{}) *IntCmd {
args := make([]interface{}, 2+len(values))
args := make([]interface{}, 2, 2+len(values))
args[0] = "lpush"
args[1] = key
for i, value := range values {
args[2+i] = value
}
args = appendArgs(args, values)
cmd := NewIntCmd(args...)
c.process(cmd)
return cmd
@@ -1076,12 +1114,10 @@ func (c *cmdable) RPopLPush(source, destination string) *StringCmd {
}
func (c *cmdable) RPush(key string, values ...interface{}) *IntCmd {
args := make([]interface{}, 2+len(values))
args := make([]interface{}, 2, 2+len(values))
args[0] = "rpush"
args[1] = key
for i, value := range values {
args[2+i] = value
}
args = appendArgs(args, values)
cmd := NewIntCmd(args...)
c.process(cmd)
return cmd
@@ -1096,12 +1132,10 @@ func (c *cmdable) RPushX(key string, value interface{}) *IntCmd {
//------------------------------------------------------------------------------
func (c *cmdable) SAdd(key string, members ...interface{}) *IntCmd {
args := make([]interface{}, 2+len(members))
args := make([]interface{}, 2, 2+len(members))
args[0] = "sadd"
args[1] = key
for i, member := range members {
args[2+i] = member
}
args = appendArgs(args, members)
cmd := NewIntCmd(args...)
c.process(cmd)
return cmd
@@ -1214,12 +1248,10 @@ func (c *cmdable) SRandMemberN(key string, count int64) *StringSliceCmd {
}
func (c *cmdable) SRem(key string, members ...interface{}) *IntCmd {
args := make([]interface{}, 2+len(members))
args := make([]interface{}, 2, 2+len(members))
args[0] = "srem"
args[1] = key
for i, member := range members {
args[2+i] = member
}
args = appendArgs(args, members)
cmd := NewIntCmd(args...)
c.process(cmd)
return cmd
@@ -1479,12 +1511,10 @@ func (c *cmdable) ZRank(key, member string) *IntCmd {
}
func (c *cmdable) ZRem(key string, members ...interface{}) *IntCmd {
args := make([]interface{}, 2+len(members))
args := make([]interface{}, 2, 2+len(members))
args[0] = "zrem"
args[1] = key
for i, member := range members {
args[2+i] = member
}
args = appendArgs(args, members)
cmd := NewIntCmd(args...)
c.process(cmd)
return cmd
@@ -1600,12 +1630,10 @@ func (c *cmdable) ZUnionStore(dest string, store ZStore, keys ...string) *IntCmd
//------------------------------------------------------------------------------
func (c *cmdable) PFAdd(key string, els ...interface{}) *IntCmd {
args := make([]interface{}, 2+len(els))
args := make([]interface{}, 2, 2+len(els))
args[0] = "pfadd"
args[1] = key
for i, el := range els {
args[2+i] = el
}
args = appendArgs(args, els)
cmd := NewIntCmd(args...)
c.process(cmd)
return cmd
@@ -1698,6 +1726,12 @@ func (c *cmdable) ConfigSet(parameter, value string) *StatusCmd {
return cmd
}
func (c *cmdable) ConfigRewrite() *StatusCmd {
cmd := NewStatusCmd("config", "rewrite")
c.process(cmd)
return cmd
}
// Deperecated. Use DBSize instead.
func (c *cmdable) DbSize() *IntCmd {
return c.DBSize()
@@ -1776,7 +1810,7 @@ func (c *cmdable) shutdown(modifier string) *StatusCmd {
}
} else {
// Server did not quit. String reply contains the reason.
cmd.err = internal.RedisError(cmd.val)
cmd.err = errors.New(cmd.val)
cmd.val = ""
}
return cmd
@@ -1817,45 +1851,39 @@ func (c *cmdable) Time() *TimeCmd {
//------------------------------------------------------------------------------
func (c *cmdable) Eval(script string, keys []string, args ...interface{}) *Cmd {
cmdArgs := make([]interface{}, 3+len(keys)+len(args))
cmdArgs := make([]interface{}, 3+len(keys), 3+len(keys)+len(args))
cmdArgs[0] = "eval"
cmdArgs[1] = script
cmdArgs[2] = len(keys)
for i, key := range keys {
cmdArgs[3+i] = key
}
pos := 3 + len(keys)
for i, arg := range args {
cmdArgs[pos+i] = arg
}
cmdArgs = appendArgs(cmdArgs, args)
cmd := NewCmd(cmdArgs...)
c.process(cmd)
return cmd
}
func (c *cmdable) EvalSha(sha1 string, keys []string, args ...interface{}) *Cmd {
cmdArgs := make([]interface{}, 3+len(keys)+len(args))
cmdArgs := make([]interface{}, 3+len(keys), 3+len(keys)+len(args))
cmdArgs[0] = "evalsha"
cmdArgs[1] = sha1
cmdArgs[2] = len(keys)
for i, key := range keys {
cmdArgs[3+i] = key
}
pos := 3 + len(keys)
for i, arg := range args {
cmdArgs[pos+i] = arg
}
cmdArgs = appendArgs(cmdArgs, args)
cmd := NewCmd(cmdArgs...)
c.process(cmd)
return cmd
}
func (c *cmdable) ScriptExists(scripts ...string) *BoolSliceCmd {
args := make([]interface{}, 2+len(scripts))
func (c *cmdable) ScriptExists(hashes ...string) *BoolSliceCmd {
args := make([]interface{}, 2+len(hashes))
args[0] = "script"
args[1] = "exists"
for i, script := range scripts {
args[2+i] = script
for i, hash := range hashes {
args[2+i] = hash
}
cmd := NewBoolSliceCmd(args...)
c.process(cmd)

2968
vendor/github.com/go-redis/redis/commands_test.go сгенерированный поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

48
vendor/github.com/go-redis/redis/example_instrumentation_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,48 +0,0 @@
package redis_test
import (
"fmt"
"github.com/go-redis/redis"
)
func Example_instrumentation() {
cl := redis.NewClient(&redis.Options{
Addr: ":6379",
})
cl.WrapProcess(func(old func(cmd redis.Cmder) error) func(cmd redis.Cmder) error {
return func(cmd redis.Cmder) error {
fmt.Printf("starting processing: <%s>\n", cmd)
err := old(cmd)
fmt.Printf("finished processing: <%s>\n", cmd)
return err
}
})
cl.Ping()
// Output: starting processing: <ping: >
// finished processing: <ping: PONG>
}
func Example_Pipeline_instrumentation() {
client := redis.NewClient(&redis.Options{
Addr: ":6379",
})
client.WrapProcessPipeline(func(old func([]redis.Cmder) error) func([]redis.Cmder) error {
return func(cmds []redis.Cmder) error {
fmt.Printf("pipeline starting processing: %v\n", cmds)
err := old(cmds)
fmt.Printf("pipeline finished processing: %v\n", cmds)
return err
}
})
client.Pipelined(func(pipe redis.Pipeliner) error {
pipe.Ping()
pipe.Ping()
return nil
})
// Output: pipeline starting processing: [ping: ping: ]
// pipeline finished processing: [ping: PONG ping: PONG]
}

414
vendor/github.com/go-redis/redis/example_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,414 +0,0 @@
package redis_test
import (
"fmt"
"strconv"
"sync"
"time"
"github.com/go-redis/redis"
)
var client *redis.Client
func init() {
client = redis.NewClient(&redis.Options{
Addr: ":6379",
DialTimeout: 10 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
PoolSize: 10,
PoolTimeout: 30 * time.Second,
})
client.FlushDB()
}
func ExampleNewClient() {
client := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
pong, err := client.Ping().Result()
fmt.Println(pong, err)
// Output: PONG <nil>
}
func ExampleParseURL() {
opt, err := redis.ParseURL("redis://:qwerty@localhost:6379/1")
if err != nil {
panic(err)
}
fmt.Println("addr is", opt.Addr)
fmt.Println("db is", opt.DB)
fmt.Println("password is", opt.Password)
// Create client as usually.
_ = redis.NewClient(opt)
// Output: addr is localhost:6379
// db is 1
// password is qwerty
}
func ExampleNewFailoverClient() {
// See http://redis.io/topics/sentinel for instructions how to
// setup Redis Sentinel.
client := redis.NewFailoverClient(&redis.FailoverOptions{
MasterName: "master",
SentinelAddrs: []string{":26379"},
})
client.Ping()
}
func ExampleNewClusterClient() {
// See http://redis.io/topics/cluster-tutorial for instructions
// how to setup Redis Cluster.
client := redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{":7000", ":7001", ":7002", ":7003", ":7004", ":7005"},
})
client.Ping()
}
func ExampleNewRing() {
client := redis.NewRing(&redis.RingOptions{
Addrs: map[string]string{
"shard1": ":7000",
"shard2": ":7001",
"shard3": ":7002",
},
})
client.Ping()
}
func ExampleClient() {
err := client.Set("key", "value", 0).Err()
if err != nil {
panic(err)
}
val, err := client.Get("key").Result()
if err != nil {
panic(err)
}
fmt.Println("key", val)
val2, err := client.Get("key2").Result()
if err == redis.Nil {
fmt.Println("key2 does not exist")
} else if err != nil {
panic(err)
} else {
fmt.Println("key2", val2)
}
// Output: key value
// key2 does not exist
}
func ExampleClient_Set() {
// Last argument is expiration. Zero means the key has no
// expiration time.
err := client.Set("key", "value", 0).Err()
if err != nil {
panic(err)
}
// key2 will expire in an hour.
err = client.Set("key2", "value", time.Hour).Err()
if err != nil {
panic(err)
}
}
func ExampleClient_Incr() {
result, err := client.Incr("counter").Result()
if err != nil {
panic(err)
}
fmt.Println(result)
// Output: 1
}
func ExampleClient_BLPop() {
if err := client.RPush("queue", "message").Err(); err != nil {
panic(err)
}
// use `client.BLPop(0, "queue")` for infinite waiting time
result, err := client.BLPop(1*time.Second, "queue").Result()
if err != nil {
panic(err)
}
fmt.Println(result[0], result[1])
// Output: queue message
}
func ExampleClient_Scan() {
client.FlushDB()
for i := 0; i < 33; i++ {
err := client.Set(fmt.Sprintf("key%d", i), "value", 0).Err()
if err != nil {
panic(err)
}
}
var cursor uint64
var n int
for {
var keys []string
var err error
keys, cursor, err = client.Scan(cursor, "", 10).Result()
if err != nil {
panic(err)
}
n += len(keys)
if cursor == 0 {
break
}
}
fmt.Printf("found %d keys\n", n)
// Output: found 33 keys
}
func ExampleClient_Pipelined() {
var incr *redis.IntCmd
_, err := client.Pipelined(func(pipe redis.Pipeliner) error {
incr = pipe.Incr("pipelined_counter")
pipe.Expire("pipelined_counter", time.Hour)
return nil
})
fmt.Println(incr.Val(), err)
// Output: 1 <nil>
}
func ExampleClient_Pipeline() {
pipe := client.Pipeline()
incr := pipe.Incr("pipeline_counter")
pipe.Expire("pipeline_counter", time.Hour)
// Execute
//
// INCR pipeline_counter
// EXPIRE pipeline_counts 3600
//
// using one client-server roundtrip.
_, err := pipe.Exec()
fmt.Println(incr.Val(), err)
// Output: 1 <nil>
}
func ExampleClient_TxPipelined() {
var incr *redis.IntCmd
_, err := client.TxPipelined(func(pipe redis.Pipeliner) error {
incr = pipe.Incr("tx_pipelined_counter")
pipe.Expire("tx_pipelined_counter", time.Hour)
return nil
})
fmt.Println(incr.Val(), err)
// Output: 1 <nil>
}
func ExampleClient_TxPipeline() {
pipe := client.TxPipeline()
incr := pipe.Incr("tx_pipeline_counter")
pipe.Expire("tx_pipeline_counter", time.Hour)
// Execute
//
// MULTI
// INCR pipeline_counter
// EXPIRE pipeline_counts 3600
// EXEC
//
// using one client-server roundtrip.
_, err := pipe.Exec()
fmt.Println(incr.Val(), err)
// Output: 1 <nil>
}
func ExampleClient_Watch() {
var incr func(string) error
// Transactionally increments key using GET and SET commands.
incr = func(key string) error {
err := client.Watch(func(tx *redis.Tx) error {
n, err := tx.Get(key).Int64()
if err != nil && err != redis.Nil {
return err
}
_, err = tx.Pipelined(func(pipe redis.Pipeliner) error {
pipe.Set(key, strconv.FormatInt(n+1, 10), 0)
return nil
})
return err
}, key)
if err == redis.TxFailedErr {
return incr(key)
}
return err
}
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
err := incr("counter3")
if err != nil {
panic(err)
}
}()
}
wg.Wait()
n, err := client.Get("counter3").Int64()
fmt.Println(n, err)
// Output: 100 <nil>
}
func ExamplePubSub() {
pubsub := client.Subscribe("mychannel1")
defer pubsub.Close()
// Wait for subscription to be created before publishing message.
subscr, err := pubsub.ReceiveTimeout(time.Second)
if err != nil {
panic(err)
}
fmt.Println(subscr)
err = client.Publish("mychannel1", "hello").Err()
if err != nil {
panic(err)
}
msg, err := pubsub.ReceiveMessage()
if err != nil {
panic(err)
}
fmt.Println(msg.Channel, msg.Payload)
// Output: subscribe: mychannel1
// mychannel1 hello
}
func ExamplePubSub_Receive() {
pubsub := client.Subscribe("mychannel2")
defer pubsub.Close()
for i := 0; i < 2; i++ {
// ReceiveTimeout is a low level API. Use ReceiveMessage instead.
msgi, err := pubsub.ReceiveTimeout(time.Second)
if err != nil {
break
}
switch msg := msgi.(type) {
case *redis.Subscription:
fmt.Println("subscribed to", msg.Channel)
_, err := client.Publish("mychannel2", "hello").Result()
if err != nil {
panic(err)
}
case *redis.Message:
fmt.Println("received", msg.Payload, "from", msg.Channel)
default:
panic("unreached")
}
}
// sent message to 1 client
// received hello from mychannel2
}
func ExampleScript() {
IncrByXX := redis.NewScript(`
if redis.call("GET", KEYS[1]) ~= false then
return redis.call("INCRBY", KEYS[1], ARGV[1])
end
return false
`)
n, err := IncrByXX.Run(client, []string{"xx_counter"}, 2).Result()
fmt.Println(n, err)
err = client.Set("xx_counter", "40", 0).Err()
if err != nil {
panic(err)
}
n, err = IncrByXX.Run(client, []string{"xx_counter"}, 2).Result()
fmt.Println(n, err)
// Output: <nil> redis: nil
// 42 <nil>
}
func Example_customCommand() {
Get := func(client *redis.Client, key string) *redis.StringCmd {
cmd := redis.NewStringCmd("get", key)
client.Process(cmd)
return cmd
}
v, err := Get(client, "key_does_not_exist").Result()
fmt.Printf("%q %s", v, err)
// Output: "" redis: nil
}
func ExampleScanIterator() {
iter := client.Scan(0, "", 0).Iterator()
for iter.Next() {
fmt.Println(iter.Val())
}
if err := iter.Err(); err != nil {
panic(err)
}
}
func ExampleScanCmd_Iterator() {
iter := client.Scan(0, "", 0).Iterator()
for iter.Next() {
fmt.Println(iter.Val())
}
if err := iter.Err(); err != nil {
panic(err)
}
}
func ExampleNewUniversalClient_simple() {
client := redis.NewUniversalClient(&redis.UniversalOptions{
Addrs: []string{":6379"},
})
defer client.Close()
client.Ping()
}
func ExampleNewUniversalClient_failover() {
client := redis.NewUniversalClient(&redis.UniversalOptions{
MasterName: "master",
Addrs: []string{":26379"},
})
defer client.Close()
client.Ping()
}
func ExampleNewUniversalClient_cluster() {
client := redis.NewUniversalClient(&redis.UniversalOptions{
Addrs: []string{":7000", ":7001", ":7002", ":7003", ":7004", ":7005"},
})
defer client.Close()
client.Ping()
}

46
vendor/github.com/go-redis/redis/export_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,46 +0,0 @@
package redis
import (
"net"
"time"
"github.com/go-redis/redis/internal/pool"
)
func (c *baseClient) Pool() pool.Pooler {
return c.connPool
}
func (c *PubSub) SetNetConn(netConn net.Conn) {
c.cn = pool.NewConn(netConn)
}
func (c *PubSub) ReceiveMessageTimeout(timeout time.Duration) (*Message, error) {
return c.receiveMessage(timeout)
}
func (c *ClusterClient) SlotAddrs(slot int) []string {
state, err := c.state()
if err != nil {
panic(err)
}
var addrs []string
for _, n := range state.slotNodes(slot) {
addrs = append(addrs, n.Client.getAddr())
}
return addrs
}
// SwapSlot swaps a slot's master/slave address for testing MOVED redirects.
func (c *ClusterClient) SwapSlotNodes(slot int) {
state, err := c.state()
if err != nil {
panic(err)
}
nodes := state.slots[slot]
if len(nodes) == 2 {
nodes[0], nodes[1] = nodes[1], nodes[0]
}
}

110
vendor/github.com/go-redis/redis/internal/consistenthash/consistenthash_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,110 +0,0 @@
/*
Copyright 2013 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package consistenthash
import (
"fmt"
"strconv"
"testing"
)
func TestHashing(t *testing.T) {
// Override the hash function to return easier to reason about values. Assumes
// the keys can be converted to an integer.
hash := New(3, func(key []byte) uint32 {
i, err := strconv.Atoi(string(key))
if err != nil {
panic(err)
}
return uint32(i)
})
// Given the above hash function, this will give replicas with "hashes":
// 2, 4, 6, 12, 14, 16, 22, 24, 26
hash.Add("6", "4", "2")
testCases := map[string]string{
"2": "2",
"11": "2",
"23": "4",
"27": "2",
}
for k, v := range testCases {
if hash.Get(k) != v {
t.Errorf("Asking for %s, should have yielded %s", k, v)
}
}
// Adds 8, 18, 28
hash.Add("8")
// 27 should now map to 8.
testCases["27"] = "8"
for k, v := range testCases {
if hash.Get(k) != v {
t.Errorf("Asking for %s, should have yielded %s", k, v)
}
}
}
func TestConsistency(t *testing.T) {
hash1 := New(1, nil)
hash2 := New(1, nil)
hash1.Add("Bill", "Bob", "Bonny")
hash2.Add("Bob", "Bonny", "Bill")
if hash1.Get("Ben") != hash2.Get("Ben") {
t.Errorf("Fetching 'Ben' from both hashes should be the same")
}
hash2.Add("Becky", "Ben", "Bobby")
if hash1.Get("Ben") != hash2.Get("Ben") ||
hash1.Get("Bob") != hash2.Get("Bob") ||
hash1.Get("Bonny") != hash2.Get("Bonny") {
t.Errorf("Direct matches should always return the same entry")
}
}
func BenchmarkGet8(b *testing.B) { benchmarkGet(b, 8) }
func BenchmarkGet32(b *testing.B) { benchmarkGet(b, 32) }
func BenchmarkGet128(b *testing.B) { benchmarkGet(b, 128) }
func BenchmarkGet512(b *testing.B) { benchmarkGet(b, 512) }
func benchmarkGet(b *testing.B, shards int) {
hash := New(50, nil)
var buckets []string
for i := 0; i < shards; i++ {
buckets = append(buckets, fmt.Sprintf("shard-%d", i))
}
hash.Add(buckets...)
b.ResetTimer()
for i := 0; i < b.N; i++ {
hash.Get(buckets[i&(shards-1)])
}
}

14
vendor/github.com/go-redis/redis/internal/error.go сгенерированный поставляемый
Просмотреть файл

@@ -4,14 +4,10 @@ import (
"io"
"net"
"strings"
"github.com/go-redis/redis/internal/proto"
)
const Nil = RedisError("redis: nil")
type RedisError string
func (e RedisError) Error() string { return string(e) }
func IsRetryableError(err error, retryNetError bool) bool {
if IsNetworkError(err) {
return retryNetError
@@ -30,7 +26,7 @@ func IsRetryableError(err error, retryNetError bool) bool {
}
func IsRedisError(err error) bool {
_, ok := err.(RedisError)
_, ok := err.(proto.RedisError)
return ok
}
@@ -42,6 +38,10 @@ func IsNetworkError(err error) bool {
return ok
}
func IsReadOnlyError(err error) bool {
return strings.HasPrefix(err.Error(), "READONLY ")
}
func IsBadConn(err error, allowTimeout bool) bool {
if err == nil {
return false

74
vendor/github.com/go-redis/redis/internal/hashtag/hashtag_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,74 +0,0 @@
package hashtag
import (
"math/rand"
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestGinkgoSuite(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "hashtag")
}
var _ = Describe("CRC16", func() {
// http://redis.io/topics/cluster-spec#keys-distribution-model
It("should calculate CRC16", func() {
tests := []struct {
s string
n uint16
}{
{"123456789", 0x31C3},
{string([]byte{83, 153, 134, 118, 229, 214, 244, 75, 140, 37, 215, 215}), 21847},
}
for _, test := range tests {
Expect(crc16sum(test.s)).To(Equal(test.n), "for %s", test.s)
}
})
})
var _ = Describe("HashSlot", func() {
It("should calculate hash slots", func() {
tests := []struct {
key string
slot int
}{
{"123456789", 12739},
{"{}foo", 9500},
{"foo{}", 5542},
{"foo{}{bar}", 8363},
{"", 10503},
{"", 5176},
{string([]byte{83, 153, 134, 118, 229, 214, 244, 75, 140, 37, 215, 215}), 5463},
}
// Empty keys receive random slot.
rand.Seed(100)
for _, test := range tests {
Expect(Slot(test.key)).To(Equal(test.slot), "for %s", test.key)
}
})
It("should extract keys from tags", func() {
tests := []struct {
one, two string
}{
{"foo{bar}", "bar"},
{"{foo}bar", "foo"},
{"{user1000}.following", "{user1000}.followers"},
{"foo{{bar}}zap", "{bar"},
{"foo{bar}{zap}", "bar"},
}
for _, test := range tests {
Expect(Slot(test.one)).To(Equal(Slot(test.two)), "for %s <-> %s", test.one, test.two)
}
})
})

18
vendor/github.com/go-redis/redis/internal/internal_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,18 +0,0 @@
package internal
import (
"testing"
"time"
. "github.com/onsi/gomega"
)
func TestRetryBackoff(t *testing.T) {
RegisterTestingT(t)
for i := -1; i <= 16; i++ {
backoff := RetryBackoff(i, time.Millisecond, 512*time.Millisecond)
Expect(backoff >= 0).To(BeTrue())
Expect(backoff <= 512*time.Millisecond).To(BeTrue())
}
}

80
vendor/github.com/go-redis/redis/internal/pool/bench_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,80 +0,0 @@
package pool_test
import (
"testing"
"time"
"github.com/go-redis/redis/internal/pool"
)
func benchmarkPoolGetPut(b *testing.B, poolSize int) {
connPool := pool.NewConnPool(&pool.Options{
Dialer: dummyDialer,
PoolSize: poolSize,
PoolTimeout: time.Second,
IdleTimeout: time.Hour,
IdleCheckFrequency: time.Hour,
})
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
cn, _, err := connPool.Get()
if err != nil {
b.Fatal(err)
}
if err = connPool.Put(cn); err != nil {
b.Fatal(err)
}
}
})
}
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) {
connPool := pool.NewConnPool(&pool.Options{
Dialer: dummyDialer,
PoolSize: poolSize,
PoolTimeout: time.Second,
IdleTimeout: time.Hour,
IdleCheckFrequency: time.Hour,
})
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
cn, _, err := connPool.Get()
if err != nil {
b.Fatal(err)
}
if err := connPool.Remove(cn); err != nil {
b.Fatal(err)
}
}
})
}
func BenchmarkPoolGetRemove10Conns(b *testing.B) {
benchmarkPoolGetRemove(b, 10)
}
func BenchmarkPoolGetRemove100Conns(b *testing.B) {
benchmarkPoolGetRemove(b, 100)
}
func BenchmarkPoolGetRemove1000Conns(b *testing.B) {
benchmarkPoolGetRemove(b, 1000)
}

35
vendor/github.com/go-redis/redis/internal/pool/main_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,35 +0,0 @@
package pool_test
import (
"net"
"sync"
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestGinkgoSuite(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "pool")
}
func perform(n int, cbs ...func(int)) {
var wg sync.WaitGroup
for _, cb := range cbs {
for i := 0; i < n; i++ {
wg.Add(1)
go func(cb func(int), i int) {
defer GinkgoRecover()
defer wg.Done()
cb(i)
}(cb, i)
}
}
wg.Wait()
}
func dummyDialer() (net.Conn, error) {
return &net.TCPConn{}, nil
}

241
vendor/github.com/go-redis/redis/internal/pool/pool_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,241 +0,0 @@
package pool_test
import (
"testing"
"time"
"github.com/go-redis/redis/internal/pool"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("ConnPool", func() {
var connPool *pool.ConnPool
BeforeEach(func() {
connPool = pool.NewConnPool(&pool.Options{
Dialer: dummyDialer,
PoolSize: 10,
PoolTimeout: time.Hour,
IdleTimeout: time.Millisecond,
IdleCheckFrequency: time.Millisecond,
})
})
AfterEach(func() {
connPool.Close()
})
It("should unblock client when conn is removed", func() {
// Reserve one connection.
cn, _, err := connPool.Get()
Expect(err).NotTo(HaveOccurred())
// Reserve all other connections.
var cns []*pool.Conn
for i := 0; i < 9; i++ {
cn, _, err := connPool.Get()
Expect(err).NotTo(HaveOccurred())
cns = append(cns, cn)
}
started := make(chan bool, 1)
done := make(chan bool, 1)
go func() {
defer GinkgoRecover()
started <- true
_, _, err := connPool.Get()
Expect(err).NotTo(HaveOccurred())
done <- true
err = connPool.Put(cn)
Expect(err).NotTo(HaveOccurred())
}()
<-started
// Check that Get is blocked.
select {
case <-done:
Fail("Get is not blocked")
default:
// ok
}
err = connPool.Remove(cn)
Expect(err).NotTo(HaveOccurred())
// Check that Ping is unblocked.
select {
case <-done:
// ok
case <-time.After(time.Second):
Fail("Get is not unblocked")
}
for _, cn := range cns {
err = connPool.Put(cn)
Expect(err).NotTo(HaveOccurred())
}
})
})
var _ = Describe("conns reaper", func() {
const idleTimeout = time.Minute
var connPool *pool.ConnPool
var conns, idleConns, closedConns []*pool.Conn
BeforeEach(func() {
conns = nil
closedConns = nil
connPool = pool.NewConnPool(&pool.Options{
Dialer: dummyDialer,
PoolSize: 10,
PoolTimeout: time.Second,
IdleTimeout: idleTimeout,
IdleCheckFrequency: time.Hour,
OnClose: func(cn *pool.Conn) error {
closedConns = append(closedConns, cn)
return nil
},
})
// add stale connections
idleConns = nil
for i := 0; i < 3; i++ {
cn, _, err := connPool.Get()
Expect(err).NotTo(HaveOccurred())
cn.SetUsedAt(time.Now().Add(-2 * idleTimeout))
conns = append(conns, cn)
idleConns = append(idleConns, cn)
}
// add fresh connections
for i := 0; i < 3; i++ {
cn, _, err := connPool.Get()
Expect(err).NotTo(HaveOccurred())
conns = append(conns, cn)
}
for _, cn := range conns {
Expect(connPool.Put(cn)).NotTo(HaveOccurred())
}
Expect(connPool.Len()).To(Equal(6))
Expect(connPool.FreeLen()).To(Equal(6))
n, err := connPool.ReapStaleConns()
Expect(err).NotTo(HaveOccurred())
Expect(n).To(Equal(3))
})
AfterEach(func() {
_ = connPool.Close()
Expect(connPool.Len()).To(Equal(0))
Expect(connPool.FreeLen()).To(Equal(0))
Expect(len(closedConns)).To(Equal(len(conns)))
Expect(closedConns).To(ConsistOf(conns))
})
It("reaps stale connections", func() {
Expect(connPool.Len()).To(Equal(3))
Expect(connPool.FreeLen()).To(Equal(3))
})
It("does not reap fresh connections", func() {
n, err := connPool.ReapStaleConns()
Expect(err).NotTo(HaveOccurred())
Expect(n).To(Equal(0))
})
It("stale connections are closed", func() {
Expect(len(closedConns)).To(Equal(len(idleConns)))
Expect(closedConns).To(ConsistOf(idleConns))
})
It("pool is functional", func() {
for j := 0; j < 3; j++ {
var freeCns []*pool.Conn
for i := 0; i < 3; i++ {
cn, _, err := connPool.Get()
Expect(err).NotTo(HaveOccurred())
Expect(cn).NotTo(BeNil())
freeCns = append(freeCns, cn)
}
Expect(connPool.Len()).To(Equal(3))
Expect(connPool.FreeLen()).To(Equal(0))
cn, _, err := connPool.Get()
Expect(err).NotTo(HaveOccurred())
Expect(cn).NotTo(BeNil())
conns = append(conns, cn)
Expect(connPool.Len()).To(Equal(4))
Expect(connPool.FreeLen()).To(Equal(0))
err = connPool.Remove(cn)
Expect(err).NotTo(HaveOccurred())
Expect(connPool.Len()).To(Equal(3))
Expect(connPool.FreeLen()).To(Equal(0))
for _, cn := range freeCns {
err := connPool.Put(cn)
Expect(err).NotTo(HaveOccurred())
}
Expect(connPool.Len()).To(Equal(3))
Expect(connPool.FreeLen()).To(Equal(3))
}
})
})
var _ = Describe("race", func() {
var connPool *pool.ConnPool
var C, N int
BeforeEach(func() {
C, N = 10, 1000
if testing.Short() {
C = 4
N = 100
}
})
AfterEach(func() {
connPool.Close()
})
It("does not happen on Get, Put, and Remove", func() {
connPool = pool.NewConnPool(&pool.Options{
Dialer: dummyDialer,
PoolSize: 10,
PoolTimeout: time.Minute,
IdleTimeout: time.Millisecond,
IdleCheckFrequency: time.Millisecond,
})
perform(C, func(id int) {
for i := 0; i < N; i++ {
cn, _, err := connPool.Get()
Expect(err).NotTo(HaveOccurred())
if err == nil {
Expect(connPool.Put(cn)).NotTo(HaveOccurred())
}
}
}, func(id int) {
for i := 0; i < N; i++ {
cn, _, err := connPool.Get()
Expect(err).NotTo(HaveOccurred())
if err == nil {
Expect(connPool.Remove(cn)).NotTo(HaveOccurred())
}
}
})
})
})

13
vendor/github.com/go-redis/redis/internal/proto/proto_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,13 +0,0 @@
package proto_test
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestGinkgoSuite(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "proto")
}

48
vendor/github.com/go-redis/redis/internal/proto/reader.go сгенерированный поставляемый
Просмотреть файл

@@ -6,7 +6,7 @@ import (
"io"
"strconv"
"github.com/go-redis/redis/internal"
"github.com/go-redis/redis/internal/util"
)
const bytesAllocLimit = 1024 * 1024 // 1mb
@@ -19,6 +19,16 @@ const (
ArrayReply = '*'
)
//------------------------------------------------------------------------------
const Nil = RedisError("redis: nil")
type RedisError string
func (e RedisError) Error() string { return string(e) }
//------------------------------------------------------------------------------
type MultiBulkParse func(*Reader, int64) (interface{}, error)
type Reader struct {
@@ -66,7 +76,7 @@ func (r *Reader) ReadLine() ([]byte, error) {
return nil, fmt.Errorf("redis: reply is empty")
}
if isNilReply(line) {
return nil, internal.Nil
return nil, Nil
}
return line, nil
}
@@ -83,7 +93,7 @@ func (r *Reader) ReadReply(m MultiBulkParse) (interface{}, error) {
case StatusReply:
return parseStatusValue(line), nil
case IntReply:
return parseInt(line[1:], 10, 64)
return util.ParseInt(line[1:], 10, 64)
case StringReply:
return r.readTmpBytesValue(line)
case ArrayReply:
@@ -105,7 +115,7 @@ func (r *Reader) ReadIntReply() (int64, error) {
case ErrorReply:
return 0, ParseErrorReply(line)
case IntReply:
return parseInt(line[1:], 10, 64)
return util.ParseInt(line[1:], 10, 64)
default:
return 0, fmt.Errorf("redis: can't parse int reply: %.100q", line)
}
@@ -151,7 +161,7 @@ func (r *Reader) ReadFloatReply() (float64, error) {
if err != nil {
return 0, err
}
return parseFloat(b, 64)
return util.ParseFloat(b, 64)
}
func (r *Reader) ReadArrayReply(m MultiBulkParse) (interface{}, error) {
@@ -221,7 +231,7 @@ func (r *Reader) ReadScanReply() ([]string, uint64, error) {
func (r *Reader) readTmpBytesValue(line []byte) ([]byte, error) {
if isNilReply(line) {
return nil, internal.Nil
return nil, Nil
}
replyLen, err := strconv.Atoi(string(line[1:]))
@@ -241,7 +251,7 @@ func (r *Reader) ReadInt() (int64, error) {
if err != nil {
return 0, err
}
return parseInt(b, 10, 64)
return util.ParseInt(b, 10, 64)
}
func (r *Reader) ReadUint() (uint64, error) {
@@ -249,7 +259,7 @@ func (r *Reader) ReadUint() (uint64, error) {
if err != nil {
return 0, err
}
return parseUint(b, 10, 64)
return util.ParseUint(b, 10, 64)
}
// --------------------------------------------------------------------
@@ -303,7 +313,7 @@ func isNilReply(b []byte) bool {
}
func ParseErrorReply(line []byte) error {
return internal.RedisError(string(line[1:]))
return RedisError(string(line[1:]))
}
func parseStatusValue(line []byte) []byte {
@@ -312,23 +322,7 @@ func parseStatusValue(line []byte) []byte {
func parseArrayLen(line []byte) (int64, error) {
if isNilReply(line) {
return 0, internal.Nil
return 0, Nil
}
return parseInt(line[1:], 10, 64)
}
func atoi(b []byte) (int, error) {
return strconv.Atoi(internal.BytesToString(b))
}
func parseInt(b []byte, base int, bitSize int) (int64, error) {
return strconv.ParseInt(internal.BytesToString(b), base, bitSize)
}
func parseUint(b []byte, base int, bitSize int) (uint64, error) {
return strconv.ParseUint(internal.BytesToString(b), base, bitSize)
}
func parseFloat(b []byte, bitSize int) (float64, error) {
return strconv.ParseFloat(internal.BytesToString(b), bitSize)
return util.ParseInt(line[1:], 10, 64)
}

87
vendor/github.com/go-redis/redis/internal/proto/reader_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,87 +0,0 @@
package proto_test
import (
"bytes"
"strings"
"testing"
"github.com/go-redis/redis/internal/proto"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Reader", func() {
It("should read n bytes", func() {
data, err := proto.NewReader(strings.NewReader("ABCDEFGHIJKLMNO")).ReadN(10)
Expect(err).NotTo(HaveOccurred())
Expect(len(data)).To(Equal(10))
Expect(string(data)).To(Equal("ABCDEFGHIJ"))
data, err = proto.NewReader(strings.NewReader(strings.Repeat("x", 8192))).ReadN(6000)
Expect(err).NotTo(HaveOccurred())
Expect(len(data)).To(Equal(6000))
})
It("should read lines", func() {
p := proto.NewReader(strings.NewReader("$5\r\nhello\r\n"))
data, err := p.ReadLine()
Expect(err).NotTo(HaveOccurred())
Expect(string(data)).To(Equal("$5"))
data, err = p.ReadLine()
Expect(err).NotTo(HaveOccurred())
Expect(string(data)).To(Equal("hello"))
})
})
func BenchmarkReader_ParseReply_Status(b *testing.B) {
benchmarkParseReply(b, "+OK\r\n", nil, false)
}
func BenchmarkReader_ParseReply_Int(b *testing.B) {
benchmarkParseReply(b, ":1\r\n", nil, false)
}
func BenchmarkReader_ParseReply_Error(b *testing.B) {
benchmarkParseReply(b, "-Error message\r\n", nil, true)
}
func BenchmarkReader_ParseReply_String(b *testing.B) {
benchmarkParseReply(b, "$5\r\nhello\r\n", nil, false)
}
func BenchmarkReader_ParseReply_Slice(b *testing.B) {
benchmarkParseReply(b, "*2\r\n$5\r\nhello\r\n$5\r\nworld\r\n", multiBulkParse, false)
}
func benchmarkParseReply(b *testing.B, reply string, m proto.MultiBulkParse, wanterr bool) {
buf := new(bytes.Buffer)
for i := 0; i < b.N; i++ {
buf.WriteString(reply)
}
p := proto.NewReader(buf)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := p.ReadReply(m)
if !wanterr && err != nil {
b.Fatal(err)
}
}
}
func multiBulkParse(p *proto.Reader, n int64) (interface{}, error) {
vv := make([]interface{}, 0, n)
for i := int64(0); i < n; i++ {
v, err := p.ReadReply(multiBulkParse)
if err != nil {
return nil, err
}
vv = append(vv, v)
}
return vv, nil
}

63
vendor/github.com/go-redis/redis/internal/proto/scan.go сгенерированный поставляемый
Просмотреть файл

@@ -5,7 +5,7 @@ import (
"fmt"
"reflect"
"github.com/go-redis/redis/internal"
"github.com/go-redis/redis/internal/util"
)
func Scan(b []byte, v interface{}) error {
@@ -13,80 +13,80 @@ func Scan(b []byte, v interface{}) error {
case nil:
return fmt.Errorf("redis: Scan(nil)")
case *string:
*v = internal.BytesToString(b)
*v = util.BytesToString(b)
return nil
case *[]byte:
*v = b
return nil
case *int:
var err error
*v, err = atoi(b)
*v, err = util.Atoi(b)
return err
case *int8:
n, err := parseInt(b, 10, 8)
n, err := util.ParseInt(b, 10, 8)
if err != nil {
return err
}
*v = int8(n)
return nil
case *int16:
n, err := parseInt(b, 10, 16)
n, err := util.ParseInt(b, 10, 16)
if err != nil {
return err
}
*v = int16(n)
return nil
case *int32:
n, err := parseInt(b, 10, 32)
n, err := util.ParseInt(b, 10, 32)
if err != nil {
return err
}
*v = int32(n)
return nil
case *int64:
n, err := parseInt(b, 10, 64)
n, err := util.ParseInt(b, 10, 64)
if err != nil {
return err
}
*v = n
return nil
case *uint:
n, err := parseUint(b, 10, 64)
n, err := util.ParseUint(b, 10, 64)
if err != nil {
return err
}
*v = uint(n)
return nil
case *uint8:
n, err := parseUint(b, 10, 8)
n, err := util.ParseUint(b, 10, 8)
if err != nil {
return err
}
*v = uint8(n)
return nil
case *uint16:
n, err := parseUint(b, 10, 16)
n, err := util.ParseUint(b, 10, 16)
if err != nil {
return err
}
*v = uint16(n)
return nil
case *uint32:
n, err := parseUint(b, 10, 32)
n, err := util.ParseUint(b, 10, 32)
if err != nil {
return err
}
*v = uint32(n)
return nil
case *uint64:
n, err := parseUint(b, 10, 64)
n, err := util.ParseUint(b, 10, 64)
if err != nil {
return err
}
*v = n
return nil
case *float32:
n, err := parseFloat(b, 32)
n, err := util.ParseFloat(b, 32)
if err != nil {
return err
}
@@ -94,7 +94,7 @@ func Scan(b []byte, v interface{}) error {
return err
case *float64:
var err error
*v, err = parseFloat(b, 64)
*v, err = util.ParseFloat(b, 64)
return err
case *bool:
*v = len(b) == 1 && b[0] == '1'
@@ -120,7 +120,7 @@ func ScanSlice(data []string, slice interface{}) error {
return fmt.Errorf("redis: ScanSlice(non-slice %T)", slice)
}
next := internal.MakeSliceNextElemFunc(v)
next := makeSliceNextElemFunc(v)
for i, s := range data {
elem := next()
if err := Scan([]byte(s), elem.Addr().Interface()); err != nil {
@@ -131,3 +131,36 @@ func ScanSlice(data []string, slice interface{}) error {
return nil
}
func makeSliceNextElemFunc(v reflect.Value) func() reflect.Value {
elemType := v.Type().Elem()
if elemType.Kind() == reflect.Ptr {
elemType = elemType.Elem()
return func() reflect.Value {
if v.Len() < v.Cap() {
v.Set(v.Slice(0, v.Len()+1))
elem := v.Index(v.Len() - 1)
if elem.IsNil() {
elem.Set(reflect.New(elemType))
}
return elem.Elem()
}
elem := reflect.New(elemType)
v.Set(reflect.Append(v, elem))
return elem.Elem()
}
}
zero := reflect.Zero(elemType)
return func() reflect.Value {
if v.Len() < v.Cap() {
v.Set(v.Slice(0, v.Len()+1))
return v.Index(v.Len() - 1)
}
v.Set(reflect.Append(v, zero))
return v.Index(v.Len() - 1)
}
}

48
vendor/github.com/go-redis/redis/internal/proto/scan_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,48 +0,0 @@
package proto
import (
"encoding/json"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
type testScanSliceStruct struct {
ID int
Name string
}
func (s *testScanSliceStruct) MarshalBinary() ([]byte, error) {
return json.Marshal(s)
}
func (s *testScanSliceStruct) UnmarshalBinary(b []byte) error {
return json.Unmarshal(b, s)
}
var _ = Describe("ScanSlice", func() {
data := []string{
`{"ID":-1,"Name":"Back Yu"}`,
`{"ID":1,"Name":"szyhf"}`,
}
It("[]testScanSliceStruct", func() {
var slice []testScanSliceStruct
err := ScanSlice(data, &slice)
Expect(err).NotTo(HaveOccurred())
Expect(slice).To(Equal([]testScanSliceStruct{
{-1, "Back Yu"},
{1, "szyhf"},
}))
})
It("var testContainer []*testScanSliceStruct", func() {
var slice []*testScanSliceStruct
err := ScanSlice(data, &slice)
Expect(err).NotTo(HaveOccurred())
Expect(slice).To(Equal([]*testScanSliceStruct{
{-1, "Back Yu"},
{1, "szyhf"},
}))
})
})

18
vendor/github.com/go-redis/redis/internal/proto/write_buffer.go сгенерированный поставляемый
Просмотреть файл

@@ -71,17 +71,15 @@ func (w *WriteBuffer) append(val interface{}) error {
} else {
w.AppendString("0")
}
default:
if bm, ok := val.(encoding.BinaryMarshaler); ok {
bb, err := bm.MarshalBinary()
if err != nil {
return err
}
w.AppendBytes(bb)
} else {
return fmt.Errorf(
"redis: can't marshal %T (consider implementing encoding.BinaryMarshaler)", val)
case encoding.BinaryMarshaler:
b, err := v.MarshalBinary()
if err != nil {
return err
}
w.AppendBytes(b)
default:
return fmt.Errorf(
"redis: can't marshal %T (consider implementing encoding.BinaryMarshaler)", val)
}
return nil
}

63
vendor/github.com/go-redis/redis/internal/proto/write_buffer_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,63 +0,0 @@
package proto_test
import (
"testing"
"time"
"github.com/go-redis/redis/internal/proto"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("WriteBuffer", func() {
var buf *proto.WriteBuffer
BeforeEach(func() {
buf = proto.NewWriteBuffer()
})
It("should reset", func() {
buf.AppendString("string")
Expect(buf.Len()).To(Equal(12))
buf.Reset()
Expect(buf.Len()).To(Equal(0))
})
It("should append args", func() {
err := buf.Append([]interface{}{
"string",
12,
34.56,
[]byte{'b', 'y', 't', 'e', 's'},
true,
nil,
})
Expect(err).NotTo(HaveOccurred())
Expect(buf.Bytes()).To(Equal([]byte("*6\r\n" +
"$6\r\nstring\r\n" +
"$2\r\n12\r\n" +
"$5\r\n34.56\r\n" +
"$5\r\nbytes\r\n" +
"$1\r\n1\r\n" +
"$0\r\n" +
"\r\n")))
})
It("should append marshalable args", func() {
err := buf.Append([]interface{}{time.Unix(1414141414, 0)})
Expect(err).NotTo(HaveOccurred())
Expect(buf.Len()).To(Equal(26))
})
})
func BenchmarkWriteBuffer_Append(b *testing.B) {
buf := proto.NewWriteBuffer()
args := []interface{}{"hello", "world", "foo", "bar"}
for i := 0; i < b.N; i++ {
buf.Append(args)
buf.Reset()
}
}

64
vendor/github.com/go-redis/redis/internal/singleflight/singleflight.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,64 @@
/*
Copyright 2013 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package singleflight provides a duplicate function call suppression
// mechanism.
package singleflight
import "sync"
// call is an in-flight or completed Do call
type call struct {
wg sync.WaitGroup
val interface{}
err error
}
// Group represents a class of work and forms a namespace in which
// units of work can be executed with duplicate suppression.
type Group struct {
mu sync.Mutex // protects m
m map[string]*call // lazily initialized
}
// Do executes and returns the results of the given function, making
// sure that only one execution is in-flight for a given key at a
// time. If a duplicate comes in, the duplicate caller waits for the
// original to complete and receives the same results.
func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
g.mu.Lock()
if g.m == nil {
g.m = make(map[string]*call)
}
if c, ok := g.m[key]; ok {
g.mu.Unlock()
c.wg.Wait()
return c.val, c.err
}
c := new(call)
c.wg.Add(1)
g.m[key] = c
g.mu.Unlock()
c.val, c.err = fn()
c.wg.Done()
g.mu.Lock()
delete(g.m, key)
g.mu.Unlock()
return c.val, c.err
}

37
vendor/github.com/go-redis/redis/internal/util.go сгенерированный поставляемый
Просмотреть файл

@@ -1,6 +1,6 @@
package internal
import "reflect"
import "github.com/go-redis/redis/internal/util"
func ToLower(s string) string {
if isLower(s) {
@@ -15,7 +15,7 @@ func ToLower(s string) string {
}
b[i] = c
}
return BytesToString(b)
return util.BytesToString(b)
}
func isLower(s string) bool {
@@ -27,36 +27,3 @@ func isLower(s string) bool {
}
return true
}
func MakeSliceNextElemFunc(v reflect.Value) func() reflect.Value {
elemType := v.Type().Elem()
if elemType.Kind() == reflect.Ptr {
elemType = elemType.Elem()
return func() reflect.Value {
if v.Len() < v.Cap() {
v.Set(v.Slice(0, v.Len()+1))
elem := v.Index(v.Len() - 1)
if elem.IsNil() {
elem.Set(reflect.New(elemType))
}
return elem.Elem()
}
elem := reflect.New(elemType)
v.Set(reflect.Append(v, elem))
return elem.Elem()
}
}
zero := reflect.Zero(elemType)
return func() reflect.Value {
if v.Len() < v.Cap() {
v.Set(v.Slice(0, v.Len()+1))
return v.Index(v.Len() - 1)
}
v.Set(reflect.Append(v, zero))
return v.Index(v.Len() - 1)
}
}

Просмотреть файл

@@ -1,6 +1,6 @@
// +build appengine
package internal
package util
func BytesToString(b []byte) string {
return string(b)

19
vendor/github.com/go-redis/redis/internal/util/strconv.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,19 @@
package util
import "strconv"
func Atoi(b []byte) (int, error) {
return strconv.Atoi(BytesToString(b))
}
func ParseInt(b []byte, base int, bitSize int) (int64, error) {
return strconv.ParseInt(BytesToString(b), base, bitSize)
}
func ParseUint(b []byte, base int, bitSize int) (uint64, error) {
return strconv.ParseUint(BytesToString(b), base, bitSize)
}
func ParseFloat(b []byte, bitSize int) (float64, error) {
return strconv.ParseFloat(BytesToString(b), bitSize)
}

Просмотреть файл

@@ -1,6 +1,6 @@
// +build !appengine
package internal
package util
import (
"unsafe"

136
vendor/github.com/go-redis/redis/iterator_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,136 +0,0 @@
package redis_test
import (
"fmt"
"github.com/go-redis/redis"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("ScanIterator", func() {
var client *redis.Client
var seed = func(n int) error {
pipe := client.Pipeline()
for i := 1; i <= n; i++ {
pipe.Set(fmt.Sprintf("K%02d", i), "x", 0).Err()
}
_, err := pipe.Exec()
return err
}
var extraSeed = func(n int, m int) error {
pipe := client.Pipeline()
for i := 1; i <= m; i++ {
pipe.Set(fmt.Sprintf("A%02d", i), "x", 0).Err()
}
for i := 1; i <= n; i++ {
pipe.Set(fmt.Sprintf("K%02d", i), "x", 0).Err()
}
_, err := pipe.Exec()
return err
}
var hashKey = "K_HASHTEST"
var hashSeed = func(n int) error {
pipe := client.Pipeline()
for i := 1; i <= n; i++ {
pipe.HSet(hashKey, fmt.Sprintf("K%02d", i), "x").Err()
}
_, err := pipe.Exec()
return err
}
BeforeEach(func() {
client = redis.NewClient(redisOptions())
Expect(client.FlushDB().Err()).NotTo(HaveOccurred())
})
AfterEach(func() {
Expect(client.Close()).NotTo(HaveOccurred())
})
It("should scan across empty DBs", func() {
iter := client.Scan(0, "", 10).Iterator()
Expect(iter.Next()).To(BeFalse())
Expect(iter.Err()).NotTo(HaveOccurred())
})
It("should scan across one page", func() {
Expect(seed(7)).NotTo(HaveOccurred())
var vals []string
iter := client.Scan(0, "", 0).Iterator()
for iter.Next() {
vals = append(vals, iter.Val())
}
Expect(iter.Err()).NotTo(HaveOccurred())
Expect(vals).To(ConsistOf([]string{"K01", "K02", "K03", "K04", "K05", "K06", "K07"}))
})
It("should scan across multiple pages", func() {
Expect(seed(71)).NotTo(HaveOccurred())
var vals []string
iter := client.Scan(0, "", 10).Iterator()
for iter.Next() {
vals = append(vals, iter.Val())
}
Expect(iter.Err()).NotTo(HaveOccurred())
Expect(vals).To(HaveLen(71))
Expect(vals).To(ContainElement("K01"))
Expect(vals).To(ContainElement("K71"))
})
It("should hscan across multiple pages", func() {
Expect(hashSeed(71)).NotTo(HaveOccurred())
var vals []string
iter := client.HScan(hashKey, 0, "", 10).Iterator()
for iter.Next() {
vals = append(vals, iter.Val())
}
Expect(iter.Err()).NotTo(HaveOccurred())
Expect(vals).To(HaveLen(71 * 2))
Expect(vals).To(ContainElement("K01"))
Expect(vals).To(ContainElement("K71"))
})
It("should scan to page borders", func() {
Expect(seed(20)).NotTo(HaveOccurred())
var vals []string
iter := client.Scan(0, "", 10).Iterator()
for iter.Next() {
vals = append(vals, iter.Val())
}
Expect(iter.Err()).NotTo(HaveOccurred())
Expect(vals).To(HaveLen(20))
})
It("should scan with match", func() {
Expect(seed(33)).NotTo(HaveOccurred())
var vals []string
iter := client.Scan(0, "K*2*", 10).Iterator()
for iter.Next() {
vals = append(vals, iter.Val())
}
Expect(iter.Err()).NotTo(HaveOccurred())
Expect(vals).To(HaveLen(13))
})
It("should scan with match across empty pages", func() {
Expect(extraSeed(2, 10)).NotTo(HaveOccurred())
var vals []string
iter := client.Scan(0, "K*", 1).Iterator()
for iter.Next() {
vals = append(vals, iter.Val())
}
Expect(iter.Err()).NotTo(HaveOccurred())
Expect(vals).To(HaveLen(2))
})
})

351
vendor/github.com/go-redis/redis/main_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,351 +0,0 @@
package redis_test
import (
"errors"
"fmt"
"net"
"os"
"os/exec"
"path/filepath"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/go-redis/redis"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
const (
redisPort = "6380"
redisAddr = ":" + redisPort
redisSecondaryPort = "6381"
)
const (
ringShard1Port = "6390"
ringShard2Port = "6391"
)
const (
sentinelName = "mymaster"
sentinelMasterPort = "8123"
sentinelSlave1Port = "8124"
sentinelSlave2Port = "8125"
sentinelPort = "8126"
)
var (
redisMain *redisProcess
ringShard1, ringShard2 *redisProcess
sentinelMaster, sentinelSlave1, sentinelSlave2, sentinel *redisProcess
)
var cluster = &clusterScenario{
ports: []string{"8220", "8221", "8222", "8223", "8224", "8225"},
nodeIds: make([]string, 6),
processes: make(map[string]*redisProcess, 6),
clients: make(map[string]*redis.Client, 6),
}
var _ = BeforeSuite(func() {
var err error
redisMain, err = startRedis(redisPort)
Expect(err).NotTo(HaveOccurred())
ringShard1, err = startRedis(ringShard1Port)
Expect(err).NotTo(HaveOccurred())
ringShard2, err = startRedis(ringShard2Port)
Expect(err).NotTo(HaveOccurred())
sentinelMaster, err = startRedis(sentinelMasterPort)
Expect(err).NotTo(HaveOccurred())
sentinel, err = startSentinel(sentinelPort, sentinelName, sentinelMasterPort)
Expect(err).NotTo(HaveOccurred())
sentinelSlave1, err = startRedis(
sentinelSlave1Port, "--slaveof", "127.0.0.1", sentinelMasterPort)
Expect(err).NotTo(HaveOccurred())
sentinelSlave2, err = startRedis(
sentinelSlave2Port, "--slaveof", "127.0.0.1", sentinelMasterPort)
Expect(err).NotTo(HaveOccurred())
Expect(startCluster(cluster)).NotTo(HaveOccurred())
})
var _ = AfterSuite(func() {
Expect(redisMain.Close()).NotTo(HaveOccurred())
Expect(ringShard1.Close()).NotTo(HaveOccurred())
Expect(ringShard2.Close()).NotTo(HaveOccurred())
Expect(sentinel.Close()).NotTo(HaveOccurred())
Expect(sentinelSlave1.Close()).NotTo(HaveOccurred())
Expect(sentinelSlave2.Close()).NotTo(HaveOccurred())
Expect(sentinelMaster.Close()).NotTo(HaveOccurred())
Expect(stopCluster(cluster)).NotTo(HaveOccurred())
})
func TestGinkgoSuite(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "go-redis")
}
//------------------------------------------------------------------------------
func redisOptions() *redis.Options {
return &redis.Options{
Addr: redisAddr,
DB: 15,
DialTimeout: 10 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
PoolSize: 10,
PoolTimeout: 30 * time.Second,
IdleTimeout: 500 * time.Millisecond,
IdleCheckFrequency: 500 * time.Millisecond,
}
}
func redisClusterOptions() *redis.ClusterOptions {
return &redis.ClusterOptions{
DialTimeout: 10 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
PoolSize: 10,
PoolTimeout: 30 * time.Second,
IdleTimeout: 500 * time.Millisecond,
IdleCheckFrequency: 500 * time.Millisecond,
}
}
func redisRingOptions() *redis.RingOptions {
return &redis.RingOptions{
Addrs: map[string]string{
"ringShardOne": ":" + ringShard1Port,
"ringShardTwo": ":" + ringShard2Port,
},
DialTimeout: 10 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
PoolSize: 10,
PoolTimeout: 30 * time.Second,
IdleTimeout: 500 * time.Millisecond,
IdleCheckFrequency: 500 * time.Millisecond,
}
}
func perform(n int, cbs ...func(int)) {
var wg sync.WaitGroup
for _, cb := range cbs {
for i := 0; i < n; i++ {
wg.Add(1)
go func(cb func(int), i int) {
defer GinkgoRecover()
defer wg.Done()
cb(i)
}(cb, i)
}
}
wg.Wait()
}
func eventually(fn func() error, timeout time.Duration) error {
var exit int32
errCh := make(chan error)
done := make(chan struct{})
go func() {
defer GinkgoRecover()
for atomic.LoadInt32(&exit) == 0 {
err := fn()
if err == nil {
close(done)
return
}
select {
case errCh <- err:
default:
}
time.Sleep(timeout / 100)
}
}()
select {
case <-done:
return nil
case <-time.After(timeout):
atomic.StoreInt32(&exit, 1)
select {
case err := <-errCh:
return err
default:
return fmt.Errorf("timeout after %s", timeout)
}
}
}
func execCmd(name string, args ...string) (*os.Process, error) {
cmd := exec.Command(name, args...)
if testing.Verbose() {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
}
return cmd.Process, cmd.Start()
}
func connectTo(port string) (*redis.Client, error) {
client := redis.NewClient(&redis.Options{
Addr: ":" + port,
})
err := eventually(func() error {
return client.Ping().Err()
}, 30*time.Second)
if err != nil {
return nil, err
}
return client, nil
}
type redisProcess struct {
*os.Process
*redis.Client
}
func (p *redisProcess) Close() error {
if err := p.Kill(); err != nil {
return err
}
err := eventually(func() error {
if err := p.Client.Ping().Err(); err != nil {
return nil
}
return errors.New("client is not shutdown")
}, 10*time.Second)
if err != nil {
return err
}
p.Client.Close()
return nil
}
var (
redisServerBin, _ = filepath.Abs(filepath.Join("testdata", "redis", "src", "redis-server"))
redisServerConf, _ = filepath.Abs(filepath.Join("testdata", "redis.conf"))
)
func redisDir(port string) (string, error) {
dir, err := filepath.Abs(filepath.Join("testdata", "instances", port))
if err != nil {
return "", err
}
if err := os.RemoveAll(dir); err != nil {
return "", err
}
if err := os.MkdirAll(dir, 0775); err != nil {
return "", err
}
return dir, nil
}
func startRedis(port string, args ...string) (*redisProcess, error) {
dir, err := redisDir(port)
if err != nil {
return nil, err
}
if err = exec.Command("cp", "-f", redisServerConf, dir).Run(); err != nil {
return nil, err
}
baseArgs := []string{filepath.Join(dir, "redis.conf"), "--port", port, "--dir", dir}
process, err := execCmd(redisServerBin, append(baseArgs, args...)...)
if err != nil {
return nil, err
}
client, err := connectTo(port)
if err != nil {
process.Kill()
return nil, err
}
return &redisProcess{process, client}, err
}
func startSentinel(port, masterName, masterPort string) (*redisProcess, error) {
dir, err := redisDir(port)
if err != nil {
return nil, err
}
process, err := execCmd(redisServerBin, os.DevNull, "--sentinel", "--port", port, "--dir", dir)
if err != nil {
return nil, err
}
client, err := connectTo(port)
if err != nil {
process.Kill()
return nil, err
}
for _, cmd := range []*redis.StatusCmd{
redis.NewStatusCmd("SENTINEL", "MONITOR", masterName, "127.0.0.1", masterPort, "1"),
redis.NewStatusCmd("SENTINEL", "SET", masterName, "down-after-milliseconds", "500"),
redis.NewStatusCmd("SENTINEL", "SET", masterName, "failover-timeout", "1000"),
redis.NewStatusCmd("SENTINEL", "SET", masterName, "parallel-syncs", "1"),
} {
client.Process(cmd)
if err := cmd.Err(); err != nil {
process.Kill()
return nil, err
}
}
return &redisProcess{process, client}, nil
}
//------------------------------------------------------------------------------
type badConnError string
func (e badConnError) Error() string { return string(e) }
func (e badConnError) Timeout() bool { return false }
func (e badConnError) Temporary() bool { return false }
type badConn struct {
net.TCPConn
readDelay, writeDelay time.Duration
readErr, writeErr error
}
var _ net.Conn = &badConn{}
func (cn *badConn) Read([]byte) (int, error) {
if cn.readDelay != 0 {
time.Sleep(cn.readDelay)
}
if cn.readErr != nil {
return 0, cn.readErr
}
return 0, badConnError("bad connection")
}
func (cn *badConn) Write([]byte) (int, error) {
if cn.writeDelay != 0 {
time.Sleep(cn.writeDelay)
}
if cn.writeErr != nil {
return 0, cn.writeErr
}
return 0, badConnError("bad connection")
}

94
vendor/github.com/go-redis/redis/options_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,94 +0,0 @@
// +build go1.7
package redis
import (
"errors"
"testing"
)
func TestParseURL(t *testing.T) {
cases := []struct {
u string
addr string
db int
tls bool
err error
}{
{
"redis://localhost:123/1",
"localhost:123",
1, false, nil,
},
{
"redis://localhost:123",
"localhost:123",
0, false, nil,
},
{
"redis://localhost/1",
"localhost:6379",
1, false, nil,
},
{
"redis://12345",
"12345:6379",
0, false, nil,
},
{
"rediss://localhost:123",
"localhost:123",
0, true, nil,
},
{
"redis://localhost/?abc=123",
"",
0, false, errors.New("no options supported"),
},
{
"http://google.com",
"",
0, false, errors.New("invalid redis URL scheme: http"),
},
{
"redis://localhost/1/2/3/4",
"",
0, false, errors.New("invalid redis URL path: /1/2/3/4"),
},
{
"12345",
"",
0, false, errors.New("invalid redis URL scheme: "),
},
{
"redis://localhost/iamadatabase",
"",
0, false, errors.New(`invalid redis database number: "iamadatabase"`),
},
}
for _, c := range cases {
t.Run(c.u, func(t *testing.T) {
o, err := ParseURL(c.u)
if c.err == nil && err != nil {
t.Fatalf("unexpected error: %q", err)
return
}
if c.err != nil && err != nil {
if c.err.Error() != err.Error() {
t.Fatalf("got %q, expected %q", err, c.err)
}
return
}
if o.Addr != c.addr {
t.Errorf("got %q, want %q", o.Addr, c.addr)
}
if o.DB != c.db {
t.Errorf("got %q, expected %q", o.DB, c.db)
}
if c.tls && o.TLSConfig == nil {
t.Errorf("got nil TLSConfig, expected a TLSConfig")
}
})
}
}

26
vendor/github.com/go-redis/redis/parser.go сгенерированный поставляемый
Просмотреть файл

@@ -14,17 +14,23 @@ func sliceParser(rd *proto.Reader, n int64) (interface{}, error) {
vals := make([]interface{}, 0, n)
for i := int64(0); i < n; i++ {
v, err := rd.ReadReply(sliceParser)
if err == Nil {
vals = append(vals, nil)
} else if err != nil {
return nil, err
} else {
switch vv := v.(type) {
case []byte:
vals = append(vals, string(vv))
default:
vals = append(vals, v)
if err != nil {
if err == Nil {
vals = append(vals, nil)
continue
}
if err, ok := err.(proto.RedisError); ok {
vals = append(vals, err)
continue
}
return nil, err
}
switch v := v.(type) {
case []byte:
vals = append(vals, string(v))
default:
vals = append(vals, v)
}
}
return vals, nil

80
vendor/github.com/go-redis/redis/pipeline_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,80 +0,0 @@
package redis_test
import (
"github.com/go-redis/redis"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("pipelining", func() {
var client *redis.Client
var pipe *redis.Pipeline
BeforeEach(func() {
client = redis.NewClient(redisOptions())
Expect(client.FlushDB().Err()).NotTo(HaveOccurred())
})
AfterEach(func() {
Expect(client.Close()).NotTo(HaveOccurred())
})
It("supports block style", func() {
var get *redis.StringCmd
cmds, err := client.Pipelined(func(pipe redis.Pipeliner) error {
get = pipe.Get("foo")
return nil
})
Expect(err).To(Equal(redis.Nil))
Expect(cmds).To(HaveLen(1))
Expect(cmds[0]).To(Equal(get))
Expect(get.Err()).To(Equal(redis.Nil))
Expect(get.Val()).To(Equal(""))
})
assertPipeline := func() {
It("returns no errors when there are no commands", func() {
_, err := pipe.Exec()
Expect(err).NotTo(HaveOccurred())
})
It("discards queued commands", func() {
pipe.Get("key")
pipe.Discard()
cmds, err := pipe.Exec()
Expect(err).NotTo(HaveOccurred())
Expect(cmds).To(BeNil())
})
It("handles val/err", func() {
err := client.Set("key", "value", 0).Err()
Expect(err).NotTo(HaveOccurred())
get := pipe.Get("key")
cmds, err := pipe.Exec()
Expect(err).NotTo(HaveOccurred())
Expect(cmds).To(HaveLen(1))
val, err := get.Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).To(Equal("value"))
})
}
Describe("Pipeline", func() {
BeforeEach(func() {
pipe = client.Pipeline().(*redis.Pipeline)
})
assertPipeline()
})
Describe("TxPipeline", func() {
BeforeEach(func() {
pipe = client.TxPipeline().(*redis.Pipeline)
})
assertPipeline()
})
})

143
vendor/github.com/go-redis/redis/pool_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,143 +0,0 @@
package redis_test
import (
"time"
"github.com/go-redis/redis"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("pool", func() {
var client *redis.Client
BeforeEach(func() {
client = redis.NewClient(redisOptions())
Expect(client.FlushDB().Err()).NotTo(HaveOccurred())
})
AfterEach(func() {
Expect(client.Close()).NotTo(HaveOccurred())
})
It("respects max size", func() {
perform(1000, func(id int) {
val, err := client.Ping().Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).To(Equal("PONG"))
})
pool := client.Pool()
Expect(pool.Len()).To(BeNumerically("<=", 10))
Expect(pool.FreeLen()).To(BeNumerically("<=", 10))
Expect(pool.Len()).To(Equal(pool.FreeLen()))
})
It("respects max size on multi", func() {
perform(1000, func(id int) {
var ping *redis.StatusCmd
err := client.Watch(func(tx *redis.Tx) error {
cmds, err := tx.Pipelined(func(pipe redis.Pipeliner) error {
ping = pipe.Ping()
return nil
})
Expect(err).NotTo(HaveOccurred())
Expect(cmds).To(HaveLen(1))
return err
})
Expect(err).NotTo(HaveOccurred())
Expect(ping.Err()).NotTo(HaveOccurred())
Expect(ping.Val()).To(Equal("PONG"))
})
pool := client.Pool()
Expect(pool.Len()).To(BeNumerically("<=", 10))
Expect(pool.FreeLen()).To(BeNumerically("<=", 10))
Expect(pool.Len()).To(Equal(pool.FreeLen()))
})
It("respects max size on pipelines", func() {
perform(1000, func(id int) {
pipe := client.Pipeline()
ping := pipe.Ping()
cmds, err := pipe.Exec()
Expect(err).NotTo(HaveOccurred())
Expect(cmds).To(HaveLen(1))
Expect(ping.Err()).NotTo(HaveOccurred())
Expect(ping.Val()).To(Equal("PONG"))
Expect(pipe.Close()).NotTo(HaveOccurred())
})
pool := client.Pool()
Expect(pool.Len()).To(BeNumerically("<=", 10))
Expect(pool.FreeLen()).To(BeNumerically("<=", 10))
Expect(pool.Len()).To(Equal(pool.FreeLen()))
})
It("removes broken connections", func() {
cn, _, err := client.Pool().Get()
Expect(err).NotTo(HaveOccurred())
cn.SetNetConn(&badConn{})
Expect(client.Pool().Put(cn)).NotTo(HaveOccurred())
err = client.Ping().Err()
Expect(err).To(MatchError("bad connection"))
val, err := client.Ping().Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).To(Equal("PONG"))
pool := client.Pool()
Expect(pool.Len()).To(Equal(1))
Expect(pool.FreeLen()).To(Equal(1))
stats := pool.Stats()
Expect(stats.Hits).To(Equal(uint32(2)))
Expect(stats.Misses).To(Equal(uint32(2)))
Expect(stats.Timeouts).To(Equal(uint32(0)))
})
It("reuses connections", func() {
for i := 0; i < 100; i++ {
val, err := client.Ping().Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).To(Equal("PONG"))
}
pool := client.Pool()
Expect(pool.Len()).To(Equal(1))
Expect(pool.FreeLen()).To(Equal(1))
stats := pool.Stats()
Expect(stats.Hits).To(Equal(uint32(100)))
Expect(stats.Misses).To(Equal(uint32(1)))
Expect(stats.Timeouts).To(Equal(uint32(0)))
})
It("removes idle connections", func() {
stats := client.PoolStats()
Expect(stats).To(Equal(&redis.PoolStats{
Hits: 0,
Misses: 1,
Timeouts: 0,
TotalConns: 1,
FreeConns: 1,
StaleConns: 0,
}))
time.Sleep(2 * time.Second)
stats = client.PoolStats()
Expect(stats).To(Equal(&redis.PoolStats{
Hits: 0,
Misses: 1,
Timeouts: 0,
TotalConns: 0,
FreeConns: 0,
StaleConns: 1,
}))
})
})

70
vendor/github.com/go-redis/redis/pubsub.go сгенерированный поставляемый
Просмотреть файл

@@ -24,8 +24,8 @@ type PubSub struct {
mu sync.Mutex
cn *pool.Conn
channels []string
patterns []string
channels map[string]struct{}
patterns map[string]struct{}
closed bool
cmd *Cmd
@@ -67,12 +67,24 @@ func (c *PubSub) _conn(channels []string) (*pool.Conn, error) {
func (c *PubSub) resubscribe(cn *pool.Conn) error {
var firstErr error
if len(c.channels) > 0 {
if err := c._subscribe(cn, "subscribe", c.channels...); err != nil && firstErr == nil {
channels := make([]string, len(c.channels))
i := 0
for channel := range c.channels {
channels[i] = channel
i++
}
if err := c._subscribe(cn, "subscribe", channels...); err != nil && firstErr == nil {
firstErr = err
}
}
if len(c.patterns) > 0 {
if err := c._subscribe(cn, "psubscribe", c.patterns...); err != nil && firstErr == nil {
patterns := make([]string, len(c.patterns))
i := 0
for pattern := range c.patterns {
patterns[i] = pattern
i++
}
if err := c._subscribe(cn, "psubscribe", patterns...); err != nil && firstErr == nil {
firstErr = err
}
}
@@ -132,7 +144,12 @@ func (c *PubSub) Close() error {
func (c *PubSub) Subscribe(channels ...string) error {
c.mu.Lock()
err := c.subscribe("subscribe", channels...)
c.channels = appendIfNotExists(c.channels, channels...)
if c.channels == nil {
c.channels = make(map[string]struct{})
}
for _, channel := range channels {
c.channels[channel] = struct{}{}
}
c.mu.Unlock()
return err
}
@@ -142,7 +159,12 @@ func (c *PubSub) Subscribe(channels ...string) error {
func (c *PubSub) PSubscribe(patterns ...string) error {
c.mu.Lock()
err := c.subscribe("psubscribe", patterns...)
c.patterns = appendIfNotExists(c.patterns, patterns...)
if c.patterns == nil {
c.patterns = make(map[string]struct{})
}
for _, pattern := range patterns {
c.patterns[pattern] = struct{}{}
}
c.mu.Unlock()
return err
}
@@ -152,7 +174,9 @@ func (c *PubSub) PSubscribe(patterns ...string) error {
func (c *PubSub) Unsubscribe(channels ...string) error {
c.mu.Lock()
err := c.subscribe("unsubscribe", channels...)
c.channels = remove(c.channels, channels...)
for _, channel := range channels {
delete(c.channels, channel)
}
c.mu.Unlock()
return err
}
@@ -162,7 +186,9 @@ func (c *PubSub) Unsubscribe(channels ...string) error {
func (c *PubSub) PUnsubscribe(patterns ...string) error {
c.mu.Lock()
err := c.subscribe("punsubscribe", patterns...)
c.patterns = remove(c.patterns, patterns...)
for _, pattern := range patterns {
delete(c.patterns, pattern)
}
c.mu.Unlock()
return err
}
@@ -371,31 +397,3 @@ func (c *PubSub) Channel() <-chan *Message {
})
return c.ch
}
func appendIfNotExists(ss []string, es ...string) []string {
loop:
for _, e := range es {
for _, s := range ss {
if s == e {
continue loop
}
}
ss = append(ss, e)
}
return ss
}
func remove(ss []string, es ...string) []string {
if len(es) == 0 {
return ss[:0]
}
for _, e := range es {
for i, s := range ss {
if s == e {
ss = append(ss[:i], ss[i+1:]...)
break
}
}
}
return ss
}

443
vendor/github.com/go-redis/redis/pubsub_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,443 +0,0 @@
package redis_test
import (
"io"
"net"
"sync"
"time"
"github.com/go-redis/redis"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("PubSub", func() {
var client *redis.Client
BeforeEach(func() {
client = redis.NewClient(redisOptions())
Expect(client.FlushDB().Err()).NotTo(HaveOccurred())
})
AfterEach(func() {
Expect(client.Close()).NotTo(HaveOccurred())
})
It("should support pattern matching", func() {
pubsub := client.PSubscribe("mychannel*")
defer pubsub.Close()
{
msgi, err := pubsub.ReceiveTimeout(time.Second)
Expect(err).NotTo(HaveOccurred())
subscr := msgi.(*redis.Subscription)
Expect(subscr.Kind).To(Equal("psubscribe"))
Expect(subscr.Channel).To(Equal("mychannel*"))
Expect(subscr.Count).To(Equal(1))
}
{
msgi, err := pubsub.ReceiveTimeout(time.Second)
Expect(err.(net.Error).Timeout()).To(Equal(true))
Expect(msgi).To(BeNil())
}
n, err := client.Publish("mychannel1", "hello").Result()
Expect(err).NotTo(HaveOccurred())
Expect(n).To(Equal(int64(1)))
Expect(pubsub.PUnsubscribe("mychannel*")).NotTo(HaveOccurred())
{
msgi, err := pubsub.ReceiveTimeout(time.Second)
Expect(err).NotTo(HaveOccurred())
subscr := msgi.(*redis.Message)
Expect(subscr.Channel).To(Equal("mychannel1"))
Expect(subscr.Pattern).To(Equal("mychannel*"))
Expect(subscr.Payload).To(Equal("hello"))
}
{
msgi, err := pubsub.ReceiveTimeout(time.Second)
Expect(err).NotTo(HaveOccurred())
subscr := msgi.(*redis.Subscription)
Expect(subscr.Kind).To(Equal("punsubscribe"))
Expect(subscr.Channel).To(Equal("mychannel*"))
Expect(subscr.Count).To(Equal(0))
}
stats := client.PoolStats()
Expect(stats.Misses).To(Equal(uint32(2)))
})
It("should pub/sub channels", func() {
channels, err := client.PubSubChannels("mychannel*").Result()
Expect(err).NotTo(HaveOccurred())
Expect(channels).To(BeEmpty())
pubsub := client.Subscribe("mychannel", "mychannel2")
defer pubsub.Close()
channels, err = client.PubSubChannels("mychannel*").Result()
Expect(err).NotTo(HaveOccurred())
Expect(channels).To(ConsistOf([]string{"mychannel", "mychannel2"}))
channels, err = client.PubSubChannels("").Result()
Expect(err).NotTo(HaveOccurred())
Expect(channels).To(BeEmpty())
channels, err = client.PubSubChannels("*").Result()
Expect(err).NotTo(HaveOccurred())
Expect(len(channels)).To(BeNumerically(">=", 2))
})
It("should return the numbers of subscribers", func() {
pubsub := client.Subscribe("mychannel", "mychannel2")
defer pubsub.Close()
channels, err := client.PubSubNumSub("mychannel", "mychannel2", "mychannel3").Result()
Expect(err).NotTo(HaveOccurred())
Expect(channels).To(Equal(map[string]int64{
"mychannel": 1,
"mychannel2": 1,
"mychannel3": 0,
}))
})
It("should return the numbers of subscribers by pattern", func() {
num, err := client.PubSubNumPat().Result()
Expect(err).NotTo(HaveOccurred())
Expect(num).To(Equal(int64(0)))
pubsub := client.PSubscribe("*")
defer pubsub.Close()
num, err = client.PubSubNumPat().Result()
Expect(err).NotTo(HaveOccurred())
Expect(num).To(Equal(int64(1)))
})
It("should pub/sub", func() {
pubsub := client.Subscribe("mychannel", "mychannel2")
defer pubsub.Close()
{
msgi, err := pubsub.ReceiveTimeout(time.Second)
Expect(err).NotTo(HaveOccurred())
subscr := msgi.(*redis.Subscription)
Expect(subscr.Kind).To(Equal("subscribe"))
Expect(subscr.Channel).To(Equal("mychannel"))
Expect(subscr.Count).To(Equal(1))
}
{
msgi, err := pubsub.ReceiveTimeout(time.Second)
Expect(err).NotTo(HaveOccurred())
subscr := msgi.(*redis.Subscription)
Expect(subscr.Kind).To(Equal("subscribe"))
Expect(subscr.Channel).To(Equal("mychannel2"))
Expect(subscr.Count).To(Equal(2))
}
{
msgi, err := pubsub.ReceiveTimeout(time.Second)
Expect(err.(net.Error).Timeout()).To(Equal(true))
Expect(msgi).NotTo(HaveOccurred())
}
n, err := client.Publish("mychannel", "hello").Result()
Expect(err).NotTo(HaveOccurred())
Expect(n).To(Equal(int64(1)))
n, err = client.Publish("mychannel2", "hello2").Result()
Expect(err).NotTo(HaveOccurred())
Expect(n).To(Equal(int64(1)))
Expect(pubsub.Unsubscribe("mychannel", "mychannel2")).NotTo(HaveOccurred())
{
msgi, err := pubsub.ReceiveTimeout(time.Second)
Expect(err).NotTo(HaveOccurred())
msg := msgi.(*redis.Message)
Expect(msg.Channel).To(Equal("mychannel"))
Expect(msg.Payload).To(Equal("hello"))
}
{
msgi, err := pubsub.ReceiveTimeout(time.Second)
Expect(err).NotTo(HaveOccurred())
msg := msgi.(*redis.Message)
Expect(msg.Channel).To(Equal("mychannel2"))
Expect(msg.Payload).To(Equal("hello2"))
}
{
msgi, err := pubsub.ReceiveTimeout(time.Second)
Expect(err).NotTo(HaveOccurred())
subscr := msgi.(*redis.Subscription)
Expect(subscr.Kind).To(Equal("unsubscribe"))
Expect(subscr.Channel).To(Equal("mychannel"))
Expect(subscr.Count).To(Equal(1))
}
{
msgi, err := pubsub.ReceiveTimeout(time.Second)
Expect(err).NotTo(HaveOccurred())
subscr := msgi.(*redis.Subscription)
Expect(subscr.Kind).To(Equal("unsubscribe"))
Expect(subscr.Channel).To(Equal("mychannel2"))
Expect(subscr.Count).To(Equal(0))
}
stats := client.PoolStats()
Expect(stats.Misses).To(Equal(uint32(2)))
})
It("should ping/pong", func() {
pubsub := client.Subscribe("mychannel")
defer pubsub.Close()
_, err := pubsub.ReceiveTimeout(time.Second)
Expect(err).NotTo(HaveOccurred())
err = pubsub.Ping("")
Expect(err).NotTo(HaveOccurred())
msgi, err := pubsub.ReceiveTimeout(time.Second)
Expect(err).NotTo(HaveOccurred())
pong := msgi.(*redis.Pong)
Expect(pong.Payload).To(Equal(""))
})
It("should ping/pong with payload", func() {
pubsub := client.Subscribe("mychannel")
defer pubsub.Close()
_, err := pubsub.ReceiveTimeout(time.Second)
Expect(err).NotTo(HaveOccurred())
err = pubsub.Ping("hello")
Expect(err).NotTo(HaveOccurred())
msgi, err := pubsub.ReceiveTimeout(time.Second)
Expect(err).NotTo(HaveOccurred())
pong := msgi.(*redis.Pong)
Expect(pong.Payload).To(Equal("hello"))
})
It("should multi-ReceiveMessage", func() {
pubsub := client.Subscribe("mychannel")
defer pubsub.Close()
subscr, err := pubsub.ReceiveTimeout(time.Second)
Expect(err).NotTo(HaveOccurred())
Expect(subscr).To(Equal(&redis.Subscription{
Kind: "subscribe",
Channel: "mychannel",
Count: 1,
}))
err = client.Publish("mychannel", "hello").Err()
Expect(err).NotTo(HaveOccurred())
err = client.Publish("mychannel", "world").Err()
Expect(err).NotTo(HaveOccurred())
msg, err := pubsub.ReceiveMessage()
Expect(err).NotTo(HaveOccurred())
Expect(msg.Channel).To(Equal("mychannel"))
Expect(msg.Payload).To(Equal("hello"))
msg, err = pubsub.ReceiveMessage()
Expect(err).NotTo(HaveOccurred())
Expect(msg.Channel).To(Equal("mychannel"))
Expect(msg.Payload).To(Equal("world"))
})
It("should ReceiveMessage after timeout", func() {
timeout := 100 * time.Millisecond
pubsub := client.Subscribe("mychannel")
defer pubsub.Close()
subscr, err := pubsub.ReceiveTimeout(time.Second)
Expect(err).NotTo(HaveOccurred())
Expect(subscr).To(Equal(&redis.Subscription{
Kind: "subscribe",
Channel: "mychannel",
Count: 1,
}))
done := make(chan bool, 1)
go func() {
defer GinkgoRecover()
defer func() {
done <- true
}()
time.Sleep(timeout + 100*time.Millisecond)
n, err := client.Publish("mychannel", "hello").Result()
Expect(err).NotTo(HaveOccurred())
Expect(n).To(Equal(int64(1)))
}()
msg, err := pubsub.ReceiveMessageTimeout(timeout)
Expect(err).NotTo(HaveOccurred())
Expect(msg.Channel).To(Equal("mychannel"))
Expect(msg.Payload).To(Equal("hello"))
Eventually(done).Should(Receive())
stats := client.PoolStats()
Expect(stats.Hits).To(Equal(uint32(1)))
Expect(stats.Misses).To(Equal(uint32(1)))
})
It("returns an error when subscribe fails", func() {
pubsub := client.Subscribe()
defer pubsub.Close()
pubsub.SetNetConn(&badConn{
readErr: io.EOF,
writeErr: io.EOF,
})
err := pubsub.Subscribe("mychannel")
Expect(err).To(MatchError("EOF"))
err = pubsub.Subscribe("mychannel")
Expect(err).NotTo(HaveOccurred())
})
expectReceiveMessageOnError := func(pubsub *redis.PubSub) {
pubsub.SetNetConn(&badConn{
readErr: io.EOF,
writeErr: io.EOF,
})
done := make(chan bool, 1)
go func() {
defer GinkgoRecover()
defer func() {
done <- true
}()
time.Sleep(100 * time.Millisecond)
err := client.Publish("mychannel", "hello").Err()
Expect(err).NotTo(HaveOccurred())
}()
msg, err := pubsub.ReceiveMessage()
Expect(err).NotTo(HaveOccurred())
Expect(msg.Channel).To(Equal("mychannel"))
Expect(msg.Payload).To(Equal("hello"))
Eventually(done).Should(Receive())
}
It("Subscribe should reconnect on ReceiveMessage error", func() {
pubsub := client.Subscribe("mychannel")
defer pubsub.Close()
subscr, err := pubsub.ReceiveTimeout(time.Second)
Expect(err).NotTo(HaveOccurred())
Expect(subscr).To(Equal(&redis.Subscription{
Kind: "subscribe",
Channel: "mychannel",
Count: 1,
}))
expectReceiveMessageOnError(pubsub)
})
It("PSubscribe should reconnect on ReceiveMessage error", func() {
pubsub := client.PSubscribe("mychannel")
defer pubsub.Close()
subscr, err := pubsub.ReceiveTimeout(time.Second)
Expect(err).NotTo(HaveOccurred())
Expect(subscr).To(Equal(&redis.Subscription{
Kind: "psubscribe",
Channel: "mychannel",
Count: 1,
}))
expectReceiveMessageOnError(pubsub)
})
It("should return on Close", func() {
pubsub := client.Subscribe("mychannel")
defer pubsub.Close()
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer GinkgoRecover()
wg.Done()
defer wg.Done()
_, err := pubsub.ReceiveMessage()
Expect(err).To(HaveOccurred())
Expect(err).To(SatisfyAny(
MatchError("redis: client is closed"),
MatchError("use of closed network connection"), // Go 1.4
))
}()
wg.Wait()
wg.Add(1)
Expect(pubsub.Close()).NotTo(HaveOccurred())
wg.Wait()
})
It("should ReceiveMessage without a subscription", func() {
timeout := 100 * time.Millisecond
pubsub := client.Subscribe()
defer pubsub.Close()
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer GinkgoRecover()
defer wg.Done()
time.Sleep(2 * timeout)
err := pubsub.Subscribe("mychannel")
Expect(err).NotTo(HaveOccurred())
time.Sleep(timeout)
err = client.Publish("mychannel", "hello").Err()
Expect(err).NotTo(HaveOccurred())
}()
msg, err := pubsub.ReceiveMessageTimeout(timeout)
Expect(err).NotTo(HaveOccurred())
Expect(msg.Channel).To(Equal("mychannel"))
Expect(msg.Payload).To(Equal("hello"))
wg.Wait()
})
It("handles big message payload", func() {
pubsub := client.Subscribe("mychannel")
defer pubsub.Close()
ch := pubsub.Channel()
bigVal := bigVal()
err := client.Publish("mychannel", bigVal).Err()
Expect(err).NotTo(HaveOccurred())
var msg *redis.Message
Eventually(ch).Should(Receive(&msg))
Expect(msg.Channel).To(Equal("mychannel"))
Expect(msg.Payload).To(Equal(string(bigVal)))
})
})

250
vendor/github.com/go-redis/redis/race_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,250 +0,0 @@
package redis_test
import (
"bytes"
"fmt"
"net"
"strconv"
"testing"
"time"
"github.com/go-redis/redis"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("races", func() {
var client *redis.Client
var C, N int
BeforeEach(func() {
client = redis.NewClient(redisOptions())
Expect(client.FlushDB().Err()).To(BeNil())
C, N = 10, 1000
if testing.Short() {
C = 4
N = 100
}
})
AfterEach(func() {
err := client.Close()
Expect(err).NotTo(HaveOccurred())
})
It("should echo", func() {
perform(C, func(id int) {
for i := 0; i < N; i++ {
msg := fmt.Sprintf("echo %d %d", id, i)
echo, err := client.Echo(msg).Result()
Expect(err).NotTo(HaveOccurred())
Expect(echo).To(Equal(msg))
}
})
})
It("should incr", func() {
key := "TestIncrFromGoroutines"
perform(C, func(id int) {
for i := 0; i < N; i++ {
err := client.Incr(key).Err()
Expect(err).NotTo(HaveOccurred())
}
})
val, err := client.Get(key).Int64()
Expect(err).NotTo(HaveOccurred())
Expect(val).To(Equal(int64(C * N)))
})
It("should handle many keys", func() {
perform(C, func(id int) {
for i := 0; i < N; i++ {
err := client.Set(
fmt.Sprintf("keys.key-%d-%d", id, i),
fmt.Sprintf("hello-%d-%d", id, i),
0,
).Err()
Expect(err).NotTo(HaveOccurred())
}
})
keys := client.Keys("keys.*")
Expect(keys.Err()).NotTo(HaveOccurred())
Expect(len(keys.Val())).To(Equal(C * N))
})
It("should handle many keys 2", func() {
perform(C, func(id int) {
keys := []string{"non-existent-key"}
for i := 0; i < N; i++ {
key := fmt.Sprintf("keys.key-%d", i)
keys = append(keys, key)
err := client.Set(key, fmt.Sprintf("hello-%d", i), 0).Err()
Expect(err).NotTo(HaveOccurred())
}
keys = append(keys, "non-existent-key")
vals, err := client.MGet(keys...).Result()
Expect(err).NotTo(HaveOccurred())
Expect(len(vals)).To(Equal(N + 2))
for i := 0; i < N; i++ {
Expect(vals[i+1]).To(Equal(fmt.Sprintf("hello-%d", i)))
}
Expect(vals[0]).To(BeNil())
Expect(vals[N+1]).To(BeNil())
})
})
It("should handle big vals in Get", func() {
C, N = 4, 100
bigVal := bigVal()
err := client.Set("key", bigVal, 0).Err()
Expect(err).NotTo(HaveOccurred())
// Reconnect to get new connection.
Expect(client.Close()).To(BeNil())
client = redis.NewClient(redisOptions())
perform(C, func(id int) {
for i := 0; i < N; i++ {
got, err := client.Get("key").Bytes()
Expect(err).NotTo(HaveOccurred())
Expect(got).To(Equal(bigVal))
}
})
})
It("should handle big vals in Set", func() {
C, N = 4, 100
bigVal := bigVal()
perform(C, func(id int) {
for i := 0; i < N; i++ {
err := client.Set("key", bigVal, 0).Err()
Expect(err).NotTo(HaveOccurred())
}
})
})
It("should select db", func() {
err := client.Set("db", 1, 0).Err()
Expect(err).NotTo(HaveOccurred())
perform(C, func(id int) {
opt := redisOptions()
opt.DB = id
client := redis.NewClient(opt)
for i := 0; i < N; i++ {
err := client.Set("db", id, 0).Err()
Expect(err).NotTo(HaveOccurred())
n, err := client.Get("db").Int64()
Expect(err).NotTo(HaveOccurred())
Expect(n).To(Equal(int64(id)))
}
err := client.Close()
Expect(err).NotTo(HaveOccurred())
})
n, err := client.Get("db").Int64()
Expect(err).NotTo(HaveOccurred())
Expect(n).To(Equal(int64(1)))
})
It("should select DB with read timeout", func() {
perform(C, func(id int) {
opt := redisOptions()
opt.DB = id
opt.ReadTimeout = time.Nanosecond
client := redis.NewClient(opt)
perform(C, func(id int) {
err := client.Ping().Err()
Expect(err).To(HaveOccurred())
Expect(err.(net.Error).Timeout()).To(BeTrue())
})
err := client.Close()
Expect(err).NotTo(HaveOccurred())
})
})
It("should Watch/Unwatch", func() {
err := client.Set("key", "0", 0).Err()
Expect(err).NotTo(HaveOccurred())
perform(C, func(id int) {
for i := 0; i < N; i++ {
err := client.Watch(func(tx *redis.Tx) error {
val, err := tx.Get("key").Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).NotTo(Equal(redis.Nil))
num, err := strconv.ParseInt(val, 10, 64)
Expect(err).NotTo(HaveOccurred())
cmds, err := tx.Pipelined(func(pipe redis.Pipeliner) error {
pipe.Set("key", strconv.FormatInt(num+1, 10), 0)
return nil
})
Expect(cmds).To(HaveLen(1))
return err
}, "key")
if err == redis.TxFailedErr {
i--
continue
}
Expect(err).NotTo(HaveOccurred())
}
})
val, err := client.Get("key").Int64()
Expect(err).NotTo(HaveOccurred())
Expect(val).To(Equal(int64(C * N)))
})
It("should Pipeline", func() {
perform(C, func(id int) {
pipe := client.Pipeline()
for i := 0; i < N; i++ {
pipe.Echo(fmt.Sprint(i))
}
cmds, err := pipe.Exec()
Expect(err).NotTo(HaveOccurred())
Expect(cmds).To(HaveLen(N))
for i := 0; i < N; i++ {
Expect(cmds[i].(*redis.StringCmd).Val()).To(Equal(fmt.Sprint(i)))
}
})
})
It("should Pipeline", func() {
pipe := client.Pipeline()
perform(N, func(id int) {
pipe.Incr("key")
})
cmds, err := pipe.Exec()
Expect(err).NotTo(HaveOccurred())
Expect(cmds).To(HaveLen(N))
n, err := client.Get("key").Int64()
Expect(err).NotTo(HaveOccurred())
Expect(n).To(Equal(int64(N)))
})
})
func bigVal() []byte {
return bytes.Repeat([]byte{'*'}, 1<<17) // 128kb
}

95
vendor/github.com/go-redis/redis/redis.go сгенерированный поставляемый
Просмотреть файл

@@ -1,6 +1,7 @@
package redis
import (
"context"
"fmt"
"log"
"os"
@@ -11,8 +12,8 @@ import (
"github.com/go-redis/redis/internal/proto"
)
// Nil reply redis returned when key does not exist.
const Nil = internal.Nil
// Nil reply Redis returns when key does not exist.
const Nil = proto.Nil
func init() {
SetLogger(log.New(os.Stderr, "redis: ", log.LstdFlags|log.Lshortfile))
@@ -22,6 +23,17 @@ func SetLogger(logger *log.Logger) {
internal.Logger = logger
}
type baseClient struct {
opt *Options
connPool pool.Pooler
process func(Cmder) error
processPipeline func([]Cmder) error
processTxPipeline func([]Cmder) error
onClose func() error // hook called when client is closed
}
func (c *baseClient) init() {
c.process = c.defaultProcess
c.processPipeline = c.defaultProcessPipeline
@@ -84,16 +96,7 @@ func (c *baseClient) initConn(cn *pool.Conn) error {
return nil
}
// Temp client to initialize connection.
conn := &Conn{
baseClient: baseClient{
opt: c.opt,
connPool: pool.NewSingleConnPool(cn),
},
}
conn.baseClient.init()
conn.statefulCmdable.setProcessor(conn.Process)
conn := newConn(c.opt, cn)
_, err := conn.Pipelined(func(pipe Pipeliner) error {
if c.opt.Password != "" {
pipe.Auth(c.opt.Password)
@@ -119,10 +122,7 @@ func (c *baseClient) initConn(cn *pool.Conn) error {
return nil
}
// WrapProcess replaces the process func. It takes a function createWrapper
// which is supplied by the user. createWrapper takes the old process func as
// an input and returns the new wrapper process func. createWrapper should
// use call the old process func within the new process func.
// WrapProcess wraps function that processes Redis commands.
func (c *baseClient) WrapProcess(fn func(oldProcess func(cmd Cmder) error) func(cmd Cmder) error) {
c.process = fn(c.process)
}
@@ -338,31 +338,50 @@ func (c *baseClient) txPipelineReadQueued(cn *pool.Conn, cmds []Cmder) error {
type Client struct {
baseClient
cmdable
}
func newClient(opt *Options, pool pool.Pooler) *Client {
c := Client{
baseClient: baseClient{
opt: opt,
connPool: pool,
},
}
c.baseClient.init()
c.cmdable.setProcessor(c.Process)
return &c
ctx context.Context
}
// NewClient returns a client to the Redis Server specified by Options.
func NewClient(opt *Options) *Client {
opt.init()
return newClient(opt, newConnPool(opt))
c := Client{
baseClient: baseClient{
opt: opt,
connPool: newConnPool(opt),
},
}
c.baseClient.init()
c.init()
return &c
}
func (c *Client) init() {
c.cmdable.setProcessor(c.Process)
}
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
}
func (c *Client) copy() *Client {
c2 := new(Client)
*c2 = *c
c2.cmdable.setProcessor(c2.Process)
return c2
cp := *c
cp.init()
return &cp
}
// Options returns read-only Options that were used to create the client.
@@ -442,6 +461,18 @@ type Conn struct {
statefulCmdable
}
func newConn(opt *Options, cn *pool.Conn) *Conn {
c := Conn{
baseClient: baseClient{
opt: opt,
connPool: pool.NewSingleConnPool(cn),
},
}
c.baseClient.init()
c.statefulCmdable.setProcessor(c.Process)
return &c
}
func (c *Conn) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
return c.Pipeline().Pipelined(fn)
}

38
vendor/github.com/go-redis/redis/redis_context.go сгенерированный поставляемый
Просмотреть файл

@@ -1,38 +0,0 @@
// +build go1.7
package redis
import (
"context"
"github.com/go-redis/redis/internal/pool"
)
type baseClient struct {
connPool pool.Pooler
opt *Options
process func(Cmder) error
processPipeline func([]Cmder) error
processTxPipeline 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
}

18
vendor/github.com/go-redis/redis/redis_no_context.go сгенерированный поставляемый
Просмотреть файл

@@ -1,18 +0,0 @@
// +build !go1.7
package redis
import (
"github.com/go-redis/redis/internal/pool"
)
type baseClient struct {
connPool pool.Pooler
opt *Options
process func(Cmder) error
processPipeline func([]Cmder) error
processTxPipeline func([]Cmder) error
onClose func() error // hook called when client is closed
}

364
vendor/github.com/go-redis/redis/redis_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,364 +0,0 @@
package redis_test
import (
"bytes"
"net"
"time"
"github.com/go-redis/redis"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Client", func() {
var client *redis.Client
BeforeEach(func() {
client = redis.NewClient(redisOptions())
Expect(client.FlushDB().Err()).NotTo(HaveOccurred())
})
AfterEach(func() {
client.Close()
})
It("should Stringer", func() {
Expect(client.String()).To(Equal("Redis<:6380 db:15>"))
})
It("should ping", func() {
val, err := client.Ping().Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).To(Equal("PONG"))
})
It("should return pool stats", func() {
Expect(client.PoolStats()).To(BeAssignableToTypeOf(&redis.PoolStats{}))
})
It("should support custom dialers", func() {
custom := redis.NewClient(&redis.Options{
Addr: ":1234",
Dialer: func() (net.Conn, error) {
return net.Dial("tcp", redisAddr)
},
})
val, err := custom.Ping().Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).To(Equal("PONG"))
Expect(custom.Close()).NotTo(HaveOccurred())
})
It("should close", func() {
Expect(client.Close()).NotTo(HaveOccurred())
err := client.Ping().Err()
Expect(err).To(MatchError("redis: client is closed"))
})
It("should close pubsub without closing the client", func() {
pubsub := client.Subscribe()
Expect(pubsub.Close()).NotTo(HaveOccurred())
_, err := pubsub.Receive()
Expect(err).To(MatchError("redis: client is closed"))
Expect(client.Ping().Err()).NotTo(HaveOccurred())
})
It("should close Tx without closing the client", func() {
err := client.Watch(func(tx *redis.Tx) error {
_, err := tx.Pipelined(func(pipe redis.Pipeliner) error {
pipe.Ping()
return nil
})
return err
})
Expect(err).NotTo(HaveOccurred())
Expect(client.Ping().Err()).NotTo(HaveOccurred())
})
It("should close pipeline without closing the client", func() {
pipeline := client.Pipeline()
Expect(pipeline.Close()).NotTo(HaveOccurred())
pipeline.Ping()
_, err := pipeline.Exec()
Expect(err).To(MatchError("redis: client is closed"))
Expect(client.Ping().Err()).NotTo(HaveOccurred())
})
It("should close pubsub when client is closed", func() {
pubsub := client.Subscribe()
Expect(client.Close()).NotTo(HaveOccurred())
_, err := pubsub.Receive()
Expect(err).To(MatchError("redis: client is closed"))
Expect(pubsub.Close()).NotTo(HaveOccurred())
})
It("should close pipeline when client is closed", func() {
pipeline := client.Pipeline()
Expect(client.Close()).NotTo(HaveOccurred())
Expect(pipeline.Close()).NotTo(HaveOccurred())
})
It("should select DB", func() {
db2 := redis.NewClient(&redis.Options{
Addr: redisAddr,
DB: 2,
})
Expect(db2.FlushDB().Err()).NotTo(HaveOccurred())
Expect(db2.Get("db").Err()).To(Equal(redis.Nil))
Expect(db2.Set("db", 2, 0).Err()).NotTo(HaveOccurred())
n, err := db2.Get("db").Int64()
Expect(err).NotTo(HaveOccurred())
Expect(n).To(Equal(int64(2)))
Expect(client.Get("db").Err()).To(Equal(redis.Nil))
Expect(db2.FlushDB().Err()).NotTo(HaveOccurred())
Expect(db2.Close()).NotTo(HaveOccurred())
})
It("processes custom commands", func() {
cmd := redis.NewCmd("PING")
client.Process(cmd)
// Flush buffers.
Expect(client.Echo("hello").Err()).NotTo(HaveOccurred())
Expect(cmd.Err()).NotTo(HaveOccurred())
Expect(cmd.Val()).To(Equal("PONG"))
})
It("should retry command on network error", func() {
Expect(client.Close()).NotTo(HaveOccurred())
client = redis.NewClient(&redis.Options{
Addr: redisAddr,
MaxRetries: 1,
})
// Put bad connection in the pool.
cn, _, err := client.Pool().Get()
Expect(err).NotTo(HaveOccurred())
cn.SetNetConn(&badConn{})
err = client.Pool().Put(cn)
Expect(err).NotTo(HaveOccurred())
err = client.Ping().Err()
Expect(err).NotTo(HaveOccurred())
})
It("should retry with backoff", func() {
Expect(client.Close()).NotTo(HaveOccurred())
// use up all the available connections to force a fail
connectionHogClient := redis.NewClient(&redis.Options{
Addr: redisAddr,
MaxRetries: 1,
})
defer connectionHogClient.Close()
for i := 0; i <= 1002; i++ {
connectionHogClient.Pool().NewConn()
}
clientNoRetry := redis.NewClient(&redis.Options{
Addr: redisAddr,
PoolSize: 1,
MaxRetryBackoff: -1,
})
defer clientNoRetry.Close()
clientRetry := redis.NewClient(&redis.Options{
Addr: redisAddr,
MaxRetries: 5,
PoolSize: 1,
MaxRetryBackoff: 128 * time.Millisecond,
})
defer clientRetry.Close()
startNoRetry := time.Now()
err := clientNoRetry.Ping().Err()
Expect(err).To(HaveOccurred())
elapseNoRetry := time.Since(startNoRetry)
startRetry := time.Now()
err = clientRetry.Ping().Err()
Expect(err).To(HaveOccurred())
elapseRetry := time.Since(startRetry)
Expect(elapseRetry > elapseNoRetry).To(BeTrue())
})
It("should update conn.UsedAt on read/write", func() {
cn, _, err := client.Pool().Get()
Expect(err).NotTo(HaveOccurred())
Expect(cn.UsedAt).NotTo(BeZero())
createdAt := cn.UsedAt()
err = client.Pool().Put(cn)
Expect(err).NotTo(HaveOccurred())
Expect(cn.UsedAt().Equal(createdAt)).To(BeTrue())
err = client.Ping().Err()
Expect(err).NotTo(HaveOccurred())
cn, _, err = client.Pool().Get()
Expect(err).NotTo(HaveOccurred())
Expect(cn).NotTo(BeNil())
Expect(cn.UsedAt().After(createdAt)).To(BeTrue())
})
It("should process command with special chars", func() {
set := client.Set("key", "hello1\r\nhello2\r\n", 0)
Expect(set.Err()).NotTo(HaveOccurred())
Expect(set.Val()).To(Equal("OK"))
get := client.Get("key")
Expect(get.Err()).NotTo(HaveOccurred())
Expect(get.Val()).To(Equal("hello1\r\nhello2\r\n"))
})
It("should handle big vals", func() {
bigVal := bytes.Repeat([]byte{'*'}, 2e6)
err := client.Set("key", bigVal, 0).Err()
Expect(err).NotTo(HaveOccurred())
// Reconnect to get new connection.
Expect(client.Close()).NotTo(HaveOccurred())
client = redis.NewClient(redisOptions())
got, err := client.Get("key").Bytes()
Expect(err).NotTo(HaveOccurred())
Expect(got).To(Equal(bigVal))
})
It("should call WrapProcess", func() {
var wrapperFnCalled bool
client.WrapProcess(func(oldProcess func(redis.Cmder) error) func(redis.Cmder) error {
return func(cmd redis.Cmder) error {
wrapperFnCalled = true
return oldProcess(cmd)
}
})
Expect(client.Ping().Err()).NotTo(HaveOccurred())
Expect(wrapperFnCalled).To(BeTrue())
})
})
var _ = Describe("Client timeout", func() {
var opt *redis.Options
var client *redis.Client
AfterEach(func() {
Expect(client.Close()).NotTo(HaveOccurred())
})
testTimeout := func() {
It("Ping timeouts", func() {
err := client.Ping().Err()
Expect(err).To(HaveOccurred())
Expect(err.(net.Error).Timeout()).To(BeTrue())
})
It("Pipeline timeouts", func() {
_, err := client.Pipelined(func(pipe redis.Pipeliner) error {
pipe.Ping()
return nil
})
Expect(err).To(HaveOccurred())
Expect(err.(net.Error).Timeout()).To(BeTrue())
})
It("Subscribe timeouts", func() {
if opt.WriteTimeout == 0 {
return
}
pubsub := client.Subscribe()
defer pubsub.Close()
err := pubsub.Subscribe("_")
Expect(err).To(HaveOccurred())
Expect(err.(net.Error).Timeout()).To(BeTrue())
})
It("Tx timeouts", func() {
err := client.Watch(func(tx *redis.Tx) error {
return tx.Ping().Err()
})
Expect(err).To(HaveOccurred())
Expect(err.(net.Error).Timeout()).To(BeTrue())
})
It("Tx Pipeline timeouts", func() {
err := client.Watch(func(tx *redis.Tx) error {
_, err := tx.Pipelined(func(pipe redis.Pipeliner) error {
pipe.Ping()
return nil
})
return err
})
Expect(err).To(HaveOccurred())
Expect(err.(net.Error).Timeout()).To(BeTrue())
})
}
Context("read timeout", func() {
BeforeEach(func() {
opt = redisOptions()
opt.ReadTimeout = time.Nanosecond
opt.WriteTimeout = -1
client = redis.NewClient(opt)
})
testTimeout()
})
Context("write timeout", func() {
BeforeEach(func() {
opt = redisOptions()
opt.ReadTimeout = -1
opt.WriteTimeout = time.Nanosecond
client = redis.NewClient(opt)
})
testTimeout()
})
})
var _ = Describe("Client OnConnect", func() {
var client *redis.Client
BeforeEach(func() {
opt := redisOptions()
opt.DB = 0
opt.OnConnect = func(cn *redis.Conn) error {
return cn.ClientSetName("on_connect").Err()
}
client = redis.NewClient(opt)
})
AfterEach(func() {
Expect(client.Close()).NotTo(HaveOccurred())
})
It("calls OnConnect", func() {
name, err := client.ClientGetName().Result()
Expect(err).NotTo(HaveOccurred())
Expect(name).To(Equal("on_connect"))
})
})

370
vendor/github.com/go-redis/redis/ring.go сгенерированный поставляемый
Просмотреть файл

@@ -1,6 +1,7 @@
package redis
import (
"context"
"errors"
"fmt"
"math/rand"
@@ -15,6 +16,8 @@ import (
"github.com/go-redis/redis/internal/pool"
)
const nreplicas = 100
var errRingShardsDown = errors.New("redis: all ring shards are down")
// RingOptions are used to configure a ring client and should be
@@ -85,6 +88,8 @@ func (opt *RingOptions) clientOptions() *Options {
}
}
//------------------------------------------------------------------------------
type ringShard struct {
Client *Client
down int32
@@ -125,6 +130,150 @@ func (shard *ringShard) Vote(up bool) bool {
return shard.IsDown()
}
//------------------------------------------------------------------------------
type ringShards struct {
mu sync.RWMutex
hash *consistenthash.Map
shards map[string]*ringShard // read only
list []*ringShard // read only
closed bool
}
func newRingShards() *ringShards {
return &ringShards{
hash: consistenthash.New(nreplicas, nil),
shards: make(map[string]*ringShard),
}
}
func (c *ringShards) Add(name string, cl *Client) {
shard := &ringShard{Client: cl}
c.hash.Add(name)
c.shards[name] = shard
c.list = append(c.list, shard)
}
func (c *ringShards) List() []*ringShard {
c.mu.RLock()
list := c.list
c.mu.RUnlock()
return list
}
func (c *ringShards) Hash(key string) string {
c.mu.RLock()
hash := c.hash.Get(key)
c.mu.RUnlock()
return hash
}
func (c *ringShards) GetByKey(key string) (*ringShard, error) {
key = hashtag.Key(key)
c.mu.RLock()
if c.closed {
c.mu.RUnlock()
return nil, pool.ErrClosed
}
hash := c.hash.Get(key)
if hash == "" {
c.mu.RUnlock()
return nil, errRingShardsDown
}
shard := c.shards[hash]
c.mu.RUnlock()
return shard, nil
}
func (c *ringShards) GetByHash(name string) (*ringShard, error) {
if name == "" {
return c.Random()
}
c.mu.RLock()
shard := c.shards[name]
c.mu.RUnlock()
return shard, nil
}
func (c *ringShards) Random() (*ringShard, error) {
return c.GetByKey(strconv.Itoa(rand.Int()))
}
// heartbeat monitors state of each shard in the ring.
func (c *ringShards) Heartbeat(frequency time.Duration) {
ticker := time.NewTicker(frequency)
defer ticker.Stop()
for range ticker.C {
var rebalance bool
c.mu.RLock()
if c.closed {
c.mu.RUnlock()
break
}
shards := c.list
c.mu.RUnlock()
for _, shard := range shards {
err := shard.Client.Ping().Err()
if shard.Vote(err == nil || err == pool.ErrPoolTimeout) {
internal.Logf("ring shard state changed: %s", shard)
rebalance = true
}
}
if rebalance {
c.rebalance()
}
}
}
// rebalance removes dead shards from the Ring.
func (c *ringShards) rebalance() {
hash := consistenthash.New(nreplicas, nil)
for name, shard := range c.shards {
if shard.IsUp() {
hash.Add(name)
}
}
c.mu.Lock()
c.hash = hash
c.mu.Unlock()
}
func (c *ringShards) Close() error {
c.mu.Lock()
defer c.mu.Unlock()
if c.closed {
return nil
}
c.closed = true
var firstErr error
for _, shard := range c.shards {
if err := shard.Client.Close(); err != nil && firstErr == nil {
firstErr = err
}
}
c.hash = nil
c.shards = nil
c.list = nil
return firstErr
}
//------------------------------------------------------------------------------
// Ring is a Redis client that uses constistent hashing to distribute
// keys across multiple Redis servers (shards). It's safe for
// concurrent use by multiple goroutines.
@@ -142,33 +291,22 @@ func (shard *ringShard) Vote(up bool) bool {
type Ring struct {
cmdable
opt *RingOptions
nreplicas int
ctx context.Context
mu sync.RWMutex
hash *consistenthash.Map
shards map[string]*ringShard
shardsList []*ringShard
opt *RingOptions
shards *ringShards
cmdsInfoCache *cmdsInfoCache
processPipeline func([]Cmder) error
cmdsInfoOnce internal.Once
cmdsInfo map[string]*CommandInfo
closed bool
}
func NewRing(opt *RingOptions) *Ring {
const nreplicas = 100
opt.init()
ring := &Ring{
opt: opt,
nreplicas: nreplicas,
hash: consistenthash.New(nreplicas, nil),
shards: make(map[string]*ringShard),
opt: opt,
shards: newRingShards(),
cmdsInfoCache: newCmdsInfoCache(),
}
ring.processPipeline = ring.defaultProcessPipeline
ring.cmdable.setProcessor(ring.Process)
@@ -176,21 +314,33 @@ func NewRing(opt *RingOptions) *Ring {
for name, addr := range opt.Addrs {
clopt := opt.clientOptions()
clopt.Addr = addr
ring.addShard(name, NewClient(clopt))
ring.shards.Add(name, NewClient(clopt))
}
go ring.heartbeat()
go ring.shards.Heartbeat(opt.HeartbeatFrequency)
return ring
}
func (c *Ring) addShard(name string, cl *Client) {
shard := &ringShard{Client: cl}
c.mu.Lock()
c.hash.Add(name)
c.shards[name] = shard
c.shardsList = append(c.shardsList, shard)
c.mu.Unlock()
func (c *Ring) Context() context.Context {
if c.ctx != nil {
return c.ctx
}
return context.Background()
}
func (c *Ring) WithContext(ctx context.Context) *Ring {
if ctx == nil {
panic("nil context")
}
c2 := c.copy()
c2.ctx = ctx
return c2
}
func (c *Ring) copy() *Ring {
cp := *c
return &cp
}
// Options returns read-only Options that were used to create the client.
@@ -204,10 +354,7 @@ func (c *Ring) retryBackoff(attempt int) time.Duration {
// PoolStats returns accumulated connection pool stats.
func (c *Ring) PoolStats() *PoolStats {
c.mu.RLock()
shards := c.shardsList
c.mu.RUnlock()
shards := c.shards.List()
var acc PoolStats
for _, shard := range shards {
s := shard.Client.connPool.Stats()
@@ -226,7 +373,7 @@ func (c *Ring) Subscribe(channels ...string) *PubSub {
panic("at least one channel is required")
}
shard, err := c.shardByKey(channels[0])
shard, err := c.shards.GetByKey(channels[0])
if err != nil {
// TODO: return PubSub with sticky error
panic(err)
@@ -240,7 +387,7 @@ func (c *Ring) PSubscribe(channels ...string) *PubSub {
panic("at least one channel is required")
}
shard, err := c.shardByKey(channels[0])
shard, err := c.shards.GetByKey(channels[0])
if err != nil {
// TODO: return PubSub with sticky error
panic(err)
@@ -251,10 +398,7 @@ func (c *Ring) PSubscribe(channels ...string) *PubSub {
// ForEachShard concurrently calls the fn on each live shard in the ring.
// It returns the first error if any.
func (c *Ring) ForEachShard(fn func(client *Client) error) error {
c.mu.RLock()
shards := c.shardsList
c.mu.RUnlock()
shards := c.shards.List()
var wg sync.WaitGroup
errCh := make(chan error, 1)
for _, shard := range shards {
@@ -285,81 +429,38 @@ func (c *Ring) ForEachShard(fn func(client *Client) error) error {
}
func (c *Ring) cmdInfo(name string) *CommandInfo {
err := c.cmdsInfoOnce.Do(func() error {
c.mu.RLock()
shards := c.shardsList
c.mu.RUnlock()
var firstErr error
cmdsInfo, err := c.cmdsInfoCache.Do(func() (map[string]*CommandInfo, error) {
shards := c.shards.List()
firstErr := errRingShardsDown
for _, shard := range shards {
cmdsInfo, err := shard.Client.Command().Result()
if err == nil {
c.cmdsInfo = cmdsInfo
return nil
return cmdsInfo, nil
}
if firstErr == nil {
firstErr = err
}
}
return firstErr
return nil, firstErr
})
if err != nil {
return nil
}
if c.cmdsInfo == nil {
return nil
}
info := c.cmdsInfo[name]
info := cmdsInfo[name]
if info == nil {
internal.Logf("info for cmd=%s not found", name)
}
return info
}
func (c *Ring) shardByKey(key string) (*ringShard, error) {
key = hashtag.Key(key)
c.mu.RLock()
if c.closed {
c.mu.RUnlock()
return nil, pool.ErrClosed
}
name := c.hash.Get(key)
if name == "" {
c.mu.RUnlock()
return nil, errRingShardsDown
}
shard := c.shards[name]
c.mu.RUnlock()
return shard, nil
}
func (c *Ring) randomShard() (*ringShard, error) {
return c.shardByKey(strconv.Itoa(rand.Int()))
}
func (c *Ring) shardByName(name string) (*ringShard, error) {
if name == "" {
return c.randomShard()
}
c.mu.RLock()
shard := c.shards[name]
c.mu.RUnlock()
return shard, nil
}
func (c *Ring) cmdShard(cmd Cmder) (*ringShard, error) {
cmdInfo := c.cmdInfo(cmd.Name())
pos := cmdFirstKeyPos(cmd, cmdInfo)
if pos == 0 {
return c.randomShard()
return c.shards.Random()
}
firstKey := cmd.stringArg(pos)
return c.shardByKey(firstKey)
return c.shards.GetByKey(firstKey)
}
func (c *Ring) WrapProcess(fn func(oldProcess func(cmd Cmder) error) func(cmd Cmder) error) {
@@ -378,77 +479,6 @@ func (c *Ring) Process(cmd Cmder) error {
return shard.Client.Process(cmd)
}
// rebalance removes dead shards from the Ring.
func (c *Ring) rebalance() {
hash := consistenthash.New(c.nreplicas, nil)
for name, shard := range c.shards {
if shard.IsUp() {
hash.Add(name)
}
}
c.mu.Lock()
c.hash = hash
c.mu.Unlock()
}
// heartbeat monitors state of each shard in the ring.
func (c *Ring) heartbeat() {
ticker := time.NewTicker(c.opt.HeartbeatFrequency)
defer ticker.Stop()
for range ticker.C {
var rebalance bool
c.mu.RLock()
if c.closed {
c.mu.RUnlock()
break
}
shards := c.shardsList
c.mu.RUnlock()
for _, shard := range shards {
err := shard.Client.Ping().Err()
if shard.Vote(err == nil || err == pool.ErrPoolTimeout) {
internal.Logf("ring shard state changed: %s", shard)
rebalance = true
}
}
if rebalance {
c.rebalance()
}
}
}
// Close closes the ring client, releasing any open resources.
//
// It is rare to Close a Ring, as the Ring is meant to be long-lived
// and shared between many goroutines.
func (c *Ring) Close() error {
c.mu.Lock()
defer c.mu.Unlock()
if c.closed {
return nil
}
c.closed = true
var firstErr error
for _, shard := range c.shards {
if err := shard.Client.Close(); err != nil && firstErr == nil {
firstErr = err
}
}
c.hash = nil
c.shards = nil
c.shardsList = nil
return firstErr
}
func (c *Ring) Pipeline() Pipeliner {
pipe := Pipeline{
exec: c.processPipeline,
@@ -471,11 +501,11 @@ func (c *Ring) defaultProcessPipeline(cmds []Cmder) error {
cmdsMap := make(map[string][]Cmder)
for _, cmd := range cmds {
cmdInfo := c.cmdInfo(cmd.Name())
name := cmd.stringArg(cmdFirstKeyPos(cmd, cmdInfo))
if name != "" {
name = c.hash.Get(hashtag.Key(name))
hash := cmd.stringArg(cmdFirstKeyPos(cmd, cmdInfo))
if hash != "" {
hash = c.shards.Hash(hashtag.Key(hash))
}
cmdsMap[name] = append(cmdsMap[name], cmd)
cmdsMap[hash] = append(cmdsMap[hash], cmd)
}
for attempt := 0; attempt <= c.opt.MaxRetries; attempt++ {
@@ -485,8 +515,8 @@ func (c *Ring) defaultProcessPipeline(cmds []Cmder) error {
var failedCmdsMap map[string][]Cmder
for name, cmds := range cmdsMap {
shard, err := c.shardByName(name)
for hash, cmds := range cmdsMap {
shard, err := c.shards.GetByHash(hash)
if err != nil {
setCmdsErr(cmds, err)
continue
@@ -509,7 +539,7 @@ func (c *Ring) defaultProcessPipeline(cmds []Cmder) error {
if failedCmdsMap == nil {
failedCmdsMap = make(map[string][]Cmder)
}
failedCmdsMap[name] = cmds
failedCmdsMap[hash] = cmds
}
}
@@ -529,3 +559,11 @@ func (c *Ring) TxPipeline() Pipeliner {
func (c *Ring) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
panic("not implemented")
}
// Close closes the ring client, releasing any open resources.
//
// It is rare to Close a Ring, as the Ring is meant to be long-lived
// and shared between many goroutines.
func (c *Ring) Close() error {
return c.shards.Close()
}

193
vendor/github.com/go-redis/redis/ring_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,193 +0,0 @@
package redis_test
import (
"crypto/rand"
"fmt"
"time"
"github.com/go-redis/redis"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Redis Ring", func() {
const heartbeat = 100 * time.Millisecond
var ring *redis.Ring
setRingKeys := func() {
for i := 0; i < 100; i++ {
err := ring.Set(fmt.Sprintf("key%d", i), "value", 0).Err()
Expect(err).NotTo(HaveOccurred())
}
}
BeforeEach(func() {
opt := redisRingOptions()
opt.HeartbeatFrequency = heartbeat
ring = redis.NewRing(opt)
err := ring.ForEachShard(func(cl *redis.Client) error {
return cl.FlushDB().Err()
})
Expect(err).NotTo(HaveOccurred())
})
AfterEach(func() {
Expect(ring.Close()).NotTo(HaveOccurred())
})
It("distributes keys", func() {
setRingKeys()
// Both shards should have some keys now.
Expect(ringShard1.Info().Val()).To(ContainSubstring("keys=57"))
Expect(ringShard2.Info().Val()).To(ContainSubstring("keys=43"))
})
It("distributes keys when using EVAL", func() {
script := redis.NewScript(`
local r = redis.call('SET', KEYS[1], ARGV[1])
return r
`)
var key string
for i := 0; i < 100; i++ {
key = fmt.Sprintf("key%d", i)
err := script.Run(ring, []string{key}, "value").Err()
Expect(err).NotTo(HaveOccurred())
}
Expect(ringShard1.Info().Val()).To(ContainSubstring("keys=57"))
Expect(ringShard2.Info().Val()).To(ContainSubstring("keys=43"))
})
It("uses single shard when one of the shards is down", func() {
// Stop ringShard2.
Expect(ringShard2.Close()).NotTo(HaveOccurred())
// Ring needs 3 * heartbeat time to detect that node is down.
// Give it more to be sure.
time.Sleep(2 * 3 * heartbeat)
setRingKeys()
// RingShard1 should have all keys.
Expect(ringShard1.Info().Val()).To(ContainSubstring("keys=100"))
// Start ringShard2.
var err error
ringShard2, err = startRedis(ringShard2Port)
Expect(err).NotTo(HaveOccurred())
// Wait for ringShard2 to come up.
Eventually(func() error {
return ringShard2.Ping().Err()
}, "1s").ShouldNot(HaveOccurred())
// Ring needs heartbeat time to detect that node is up.
// Give it more to be sure.
time.Sleep(heartbeat + heartbeat)
setRingKeys()
// RingShard2 should have its keys.
Expect(ringShard2.Info().Val()).To(ContainSubstring("keys=43"))
})
It("supports hash tags", func() {
for i := 0; i < 100; i++ {
err := ring.Set(fmt.Sprintf("key%d{tag}", i), "value", 0).Err()
Expect(err).NotTo(HaveOccurred())
}
Expect(ringShard1.Info().Val()).ToNot(ContainSubstring("keys="))
Expect(ringShard2.Info().Val()).To(ContainSubstring("keys=100"))
})
Describe("pipeline", func() {
It("distributes keys", func() {
pipe := ring.Pipeline()
for i := 0; i < 100; i++ {
err := pipe.Set(fmt.Sprintf("key%d", i), "value", 0).Err()
Expect(err).NotTo(HaveOccurred())
}
cmds, err := pipe.Exec()
Expect(err).NotTo(HaveOccurred())
Expect(cmds).To(HaveLen(100))
Expect(pipe.Close()).NotTo(HaveOccurred())
for _, cmd := range cmds {
Expect(cmd.Err()).NotTo(HaveOccurred())
Expect(cmd.(*redis.StatusCmd).Val()).To(Equal("OK"))
}
// Both shards should have some keys now.
Expect(ringShard1.Info().Val()).To(ContainSubstring("keys=57"))
Expect(ringShard2.Info().Val()).To(ContainSubstring("keys=43"))
})
It("is consistent with ring", func() {
var keys []string
for i := 0; i < 100; i++ {
key := make([]byte, 64)
_, err := rand.Read(key)
Expect(err).NotTo(HaveOccurred())
keys = append(keys, string(key))
}
_, err := ring.Pipelined(func(pipe redis.Pipeliner) error {
for _, key := range keys {
pipe.Set(key, "value", 0).Err()
}
return nil
})
Expect(err).NotTo(HaveOccurred())
for _, key := range keys {
val, err := ring.Get(key).Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).To(Equal("value"))
}
})
It("supports hash tags", func() {
_, err := ring.Pipelined(func(pipe redis.Pipeliner) error {
for i := 0; i < 100; i++ {
pipe.Set(fmt.Sprintf("key%d{tag}", i), "value", 0).Err()
}
return nil
})
Expect(err).NotTo(HaveOccurred())
Expect(ringShard1.Info().Val()).ToNot(ContainSubstring("keys="))
Expect(ringShard2.Info().Val()).To(ContainSubstring("keys=100"))
})
})
})
var _ = Describe("empty Redis Ring", func() {
var ring *redis.Ring
BeforeEach(func() {
ring = redis.NewRing(&redis.RingOptions{})
})
AfterEach(func() {
Expect(ring.Close()).NotTo(HaveOccurred())
})
It("returns an error", func() {
err := ring.Ping().Err()
Expect(err).To(MatchError("redis: all ring shards are down"))
})
It("pipeline returns an error", func() {
_, err := ring.Pipelined(func(pipe redis.Pipeliner) error {
pipe.Ping()
return nil
})
Expect(err).To(MatchError("redis: all ring shards are down"))
})
})

4
vendor/github.com/go-redis/redis/script.go сгенерированный поставляемый
Просмотреть файл

@@ -10,7 +10,7 @@ import (
type scripter interface {
Eval(script string, keys []string, args ...interface{}) *Cmd
EvalSha(sha1 string, keys []string, args ...interface{}) *Cmd
ScriptExists(scripts ...string) *BoolSliceCmd
ScriptExists(hashes ...string) *BoolSliceCmd
ScriptLoad(script string) *StringCmd
}
@@ -40,7 +40,7 @@ func (s *Script) Load(c scripter) *StringCmd {
}
func (s *Script) Exists(c scripter) *BoolSliceCmd {
return c.ScriptExists(s.src)
return c.ScriptExists(s.hash)
}
func (s *Script) Eval(c scripter, keys []string, args ...interface{}) *Cmd {

88
vendor/github.com/go-redis/redis/sentinel_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,88 +0,0 @@
package redis_test
import (
"github.com/go-redis/redis"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Sentinel", func() {
var client *redis.Client
BeforeEach(func() {
client = redis.NewFailoverClient(&redis.FailoverOptions{
MasterName: sentinelName,
SentinelAddrs: []string{":" + sentinelPort},
})
Expect(client.FlushDB().Err()).NotTo(HaveOccurred())
})
AfterEach(func() {
Expect(client.Close()).NotTo(HaveOccurred())
})
It("should facilitate failover", func() {
// Set value on master.
err := client.Set("foo", "master", 0).Err()
Expect(err).NotTo(HaveOccurred())
// Verify.
val, err := sentinelMaster.Get("foo").Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).To(Equal("master"))
// Create subscription.
ch := client.Subscribe("foo").Channel()
// Wait until replicated.
Eventually(func() string {
return sentinelSlave1.Get("foo").Val()
}, "1s", "100ms").Should(Equal("master"))
Eventually(func() string {
return sentinelSlave2.Get("foo").Val()
}, "1s", "100ms").Should(Equal("master"))
// Wait until slaves are picked up by sentinel.
Eventually(func() string {
return sentinel.Info().Val()
}, "10s", "100ms").Should(ContainSubstring("slaves=2"))
// Kill master.
sentinelMaster.Shutdown()
Eventually(func() error {
return sentinelMaster.Ping().Err()
}, "5s", "100ms").Should(HaveOccurred())
// Wait for Redis sentinel to elect new master.
Eventually(func() string {
return sentinelSlave1.Info().Val() + sentinelSlave2.Info().Val()
}, "30s", "1s").Should(ContainSubstring("role:master"))
// Check that client picked up new master.
Eventually(func() error {
return client.Get("foo").Err()
}, "5s", "100ms").ShouldNot(HaveOccurred())
// Publish message to check if subscription is renewed.
err = client.Publish("foo", "hello").Err()
Expect(err).NotTo(HaveOccurred())
var msg *redis.Message
Eventually(ch).Should(Receive(&msg))
Expect(msg.Channel).To(Equal("foo"))
Expect(msg.Payload).To(Equal("hello"))
})
It("supports DB selection", func() {
Expect(client.Close()).NotTo(HaveOccurred())
client = redis.NewFailoverClient(&redis.FailoverOptions{
MasterName: sentinelName,
SentinelAddrs: []string{":" + sentinelPort},
DB: 1,
})
err := client.Ping().Err()
Expect(err).NotTo(HaveOccurred())
})
})

10
vendor/github.com/go-redis/redis/testdata/redis.conf сгенерированный поставляемый
Просмотреть файл

@@ -1,10 +0,0 @@
# Minimal redis.conf
port 6379
daemonize no
dir .
save ""
appendonly yes
cluster-config-file nodes.conf
cluster-node-timeout 30000
maxclients 1001

4
vendor/github.com/go-redis/redis/tx.go сгенерированный поставляемый
Просмотреть файл

@@ -1,12 +1,12 @@
package redis
import (
"github.com/go-redis/redis/internal"
"github.com/go-redis/redis/internal/pool"
"github.com/go-redis/redis/internal/proto"
)
// TxFailedErr transaction redis failed.
const TxFailedErr = internal.RedisError("redis: transaction failed")
const TxFailedErr = proto.RedisError("redis: transaction failed")
// Tx implements Redis transactions as described in
// http://redis.io/topics/transactions. It's NOT safe for concurrent use

151
vendor/github.com/go-redis/redis/tx_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,151 +0,0 @@
package redis_test
import (
"strconv"
"sync"
"github.com/go-redis/redis"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Tx", func() {
var client *redis.Client
BeforeEach(func() {
client = redis.NewClient(redisOptions())
Expect(client.FlushDB().Err()).NotTo(HaveOccurred())
})
AfterEach(func() {
Expect(client.Close()).NotTo(HaveOccurred())
})
It("should Watch", func() {
var incr func(string) error
// Transactionally increments key using GET and SET commands.
incr = func(key string) error {
err := client.Watch(func(tx *redis.Tx) error {
n, err := tx.Get(key).Int64()
if err != nil && err != redis.Nil {
return err
}
_, err = tx.Pipelined(func(pipe redis.Pipeliner) error {
pipe.Set(key, strconv.FormatInt(n+1, 10), 0)
return nil
})
return err
}, key)
if err == redis.TxFailedErr {
return incr(key)
}
return err
}
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer GinkgoRecover()
defer wg.Done()
err := incr("key")
Expect(err).NotTo(HaveOccurred())
}()
}
wg.Wait()
n, err := client.Get("key").Int64()
Expect(err).NotTo(HaveOccurred())
Expect(n).To(Equal(int64(100)))
})
It("should discard", func() {
err := client.Watch(func(tx *redis.Tx) error {
cmds, err := tx.Pipelined(func(pipe redis.Pipeliner) error {
pipe.Set("key1", "hello1", 0)
pipe.Discard()
pipe.Set("key2", "hello2", 0)
return nil
})
Expect(err).NotTo(HaveOccurred())
Expect(cmds).To(HaveLen(1))
return err
}, "key1", "key2")
Expect(err).NotTo(HaveOccurred())
get := client.Get("key1")
Expect(get.Err()).To(Equal(redis.Nil))
Expect(get.Val()).To(Equal(""))
get = client.Get("key2")
Expect(get.Err()).NotTo(HaveOccurred())
Expect(get.Val()).To(Equal("hello2"))
})
It("returns no error when there are no commands", func() {
err := client.Watch(func(tx *redis.Tx) error {
_, err := tx.Pipelined(func(redis.Pipeliner) error { return nil })
return err
})
Expect(err).NotTo(HaveOccurred())
v, err := client.Ping().Result()
Expect(err).NotTo(HaveOccurred())
Expect(v).To(Equal("PONG"))
})
It("should exec bulks", func() {
const N = 20000
err := client.Watch(func(tx *redis.Tx) error {
cmds, err := tx.Pipelined(func(pipe redis.Pipeliner) error {
for i := 0; i < N; i++ {
pipe.Incr("key")
}
return nil
})
Expect(err).NotTo(HaveOccurred())
Expect(len(cmds)).To(Equal(N))
for _, cmd := range cmds {
Expect(cmd.Err()).NotTo(HaveOccurred())
}
return err
})
Expect(err).NotTo(HaveOccurred())
num, err := client.Get("key").Int64()
Expect(err).NotTo(HaveOccurred())
Expect(num).To(Equal(int64(N)))
})
It("should recover from bad connection", func() {
// Put bad connection in the pool.
cn, _, err := client.Pool().Get()
Expect(err).NotTo(HaveOccurred())
cn.SetNetConn(&badConn{})
err = client.Pool().Put(cn)
Expect(err).NotTo(HaveOccurred())
do := func() error {
err := client.Watch(func(tx *redis.Tx) error {
_, err := tx.Pipelined(func(pipe redis.Pipeliner) error {
pipe.Ping()
return nil
})
return err
})
return err
}
err = do()
Expect(err).To(MatchError("bad connection"))
err = do()
Expect(err).NotTo(HaveOccurred())
})
})

1
vendor/github.com/go-redis/redis/universal.go сгенерированный поставляемый
Просмотреть файл

@@ -114,6 +114,7 @@ func (o *UniversalOptions) simple() *Options {
type UniversalClient interface {
Cmdable
Process(cmd Cmder) error
WrapProcess(fn func(oldProcess func(cmd Cmder) error) func(cmd Cmder) error)
Subscribe(channels ...string) *PubSub
PSubscribe(channels ...string) *PubSub
Close() error

41
vendor/github.com/go-redis/redis/universal_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1,41 +0,0 @@
package redis_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/go-redis/redis"
)
var _ = Describe("UniversalClient", func() {
var client redis.UniversalClient
AfterEach(func() {
if client != nil {
Expect(client.Close()).To(Succeed())
}
})
It("should connect to failover servers", func() {
client = redis.NewUniversalClient(&redis.UniversalOptions{
MasterName: sentinelName,
Addrs: []string{":" + sentinelPort},
})
Expect(client.Ping().Err()).NotTo(HaveOccurred())
})
It("should connect to simple servers", func() {
client = redis.NewUniversalClient(&redis.UniversalOptions{
Addrs: []string{redisAddr},
})
Expect(client.Ping().Err()).NotTo(HaveOccurred())
})
It("should connect to clusters", func() {
client = redis.NewUniversalClient(&redis.UniversalOptions{
Addrs: cluster.addrs(),
})
Expect(client.Ping().Err()).NotTo(HaveOccurred())
})
})