/
redsync.go
125 lines (104 loc) · 2.76 KB
/
redsync.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package gousuredis
// From https://github.com/go-redsync/redsync/
import (
"context"
"fmt"
"strings"
"time"
redsyncredis "github.com/go-redsync/redsync/v4/redis"
"github.com/gomodule/redigo/redis"
"github.com/mna/redisc"
)
type redsyncPool struct {
cluster *redisc.Cluster
pool *redis.Pool
withRetry bool
}
func (p *redsyncPool) Get(ctx context.Context) (redsyncredis.Conn, error) {
var c redis.Conn
var err error
if p.cluster != nil {
c = p.cluster.Get()
} else {
if ctx != nil {
c, err = p.pool.GetContext(ctx)
if err != nil {
return nil, err
}
} else {
c = p.pool.Get()
}
}
if p.withRetry {
rc, err := redisc.RetryConn(c, 3, 100*time.Millisecond)
if err != nil {
return nil, fmt.Errorf("retry failed: %s", err)
}
return &conn{rc}, nil
}
return &conn{c}, nil
}
func newRedsyncPoolFromPool(p *redis.Pool) redsyncredis.Pool {
return &redsyncPool{
cluster: nil,
pool: p,
withRetry: false,
}
}
func newRedsyncPoolFromCluster(c *redisc.Cluster) redsyncredis.Pool {
return &redsyncPool{
pool: nil,
cluster: c,
withRetry: true,
}
}
type conn struct {
delegate redis.Conn
}
func (c *conn) Get(name string) (string, error) {
value, err := redis.String(c.delegate.Do("GET", name))
return value, noErrNil(err)
}
func (c *conn) Set(name string, value string) (bool, error) {
reply, err := redis.String(c.delegate.Do("SET", name, value))
return reply == "OK", noErrNil(err)
}
func (c *conn) SetNX(name string, value string, expiry time.Duration) (bool, error) {
reply, err := redis.String(c.delegate.Do("SET", name, value, "NX", "PX", int(expiry/time.Millisecond)))
return reply == "OK", noErrNil(err)
}
func (c *conn) PTTL(name string) (time.Duration, error) {
expiry, err := redis.Int64(c.delegate.Do("PTTL", name))
return time.Duration(expiry) * time.Millisecond, noErrNil(err)
}
func (c *conn) Eval(script *redsyncredis.Script, keysAndArgs ...interface{}) (interface{}, error) {
v, err := c.delegate.Do("EVALSHA", args(script, script.Hash, keysAndArgs)...)
if e, ok := err.(redis.Error); ok && strings.HasPrefix(string(e), "NOSCRIPT ") {
v, err = c.delegate.Do("EVAL", args(script, script.Src, keysAndArgs)...)
}
return v, noErrNil(err)
}
func (c *conn) Close() error {
err := c.delegate.Close()
return noErrNil(err)
}
func noErrNil(err error) error {
if err == redis.ErrNil {
return nil
}
return err
}
func args(script *redsyncredis.Script, spec string, keysAndArgs []interface{}) []interface{} {
var args []interface{}
if script.KeyCount < 0 {
args = make([]interface{}, 1+len(keysAndArgs))
args[0] = spec
copy(args[1:], keysAndArgs)
} else {
args = make([]interface{}, 2+len(keysAndArgs))
args[0] = spec
args[1] = script.KeyCount
copy(args[2:], keysAndArgs)
}
return args
}