-
Notifications
You must be signed in to change notification settings - Fork 113
/
radix.go
193 lines (187 loc) · 7.06 KB
/
radix.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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
// Package radix implements all functionality needed to work with redis and all
// things related to it, including redis cluster, pubsub, sentinel, scanning,
// lua scripting, and more.
//
// Creating a client
//
// For a single node redis instance use NewPool to create a connection pool. The
// connection pool is thread-safe and will automatically create, reuse, and
// recreate connections as needed:
//
// pool, err := radix.NewPool("tcp", "127.0.0.1:6379", 10)
// if err != nil {
// // handle error
// }
//
// If you're using sentinel or cluster you should use NewSentinel or NewCluster
// (respectively) to create your client instead.
//
// Commands
//
// Any redis command can be performed by passing a Cmd into a Client's Do
// method. Each Cmd should only be used once. The return from the Cmd can be
// captured into any appopriate go primitive type, or a slice, map, or struct,
// if the command returns an array.
//
// err := client.Do(radix.Cmd(nil, "SET", "foo", "someval"))
//
// var fooVal string
// err := client.Do(radix.Cmd(&fooVal, "GET", "foo"))
//
// var fooValB []byte
// err := client.Do(radix.Cmd(&fooValB, "GET", "foo"))
//
// var barI int
// err := client.Do(radix.Cmd(&barI, "INCR", "bar"))
//
// var bazEls []string
// err := client.Do(radix.Cmd(&bazEls, "LRANGE", "baz", "0", "-1"))
//
// var buzMap map[string]string
// err := client.Do(radix.Cmd(&buzMap, "HGETALL", "buz"))
//
// FlatCmd can also be used if you wish to use non-string arguments like
// integers, slices, maps, or structs, and have them automatically be flattened
// into a single string slice.
//
// Struct Scanning
//
// Cmd and FlatCmd can unmarshal results into a struct. The results must be a
// key/value array, such as that returned by HGETALL. Exported field names will
// be used as keys, unless the fields have the "redis" tag:
//
// type MyType struct {
// Foo string // Will be populated with the value for key "Foo"
// Bar string `redis:"BAR"` // Will be populated with the value for key "BAR"
// Baz string `redis:"-"` // Will not be populated
// }
//
// Embedded structs will inline that struct's fields into the parent's:
//
// type MyOtherType struct {
// // adds fields "Foo" and "BAR" (from above example) to MyOtherType
// MyType
// Biz int
// }
//
// The same rules for field naming apply when a struct is passed into FlatCmd as
// an argument.
//
// Actions
//
// Cmd and FlatCmd both implement the Action interface. Other Actions include
// Pipeline, WithConn, and EvalScript.Cmd. Any of these may be passed into any
// Client's Do method.
//
// var fooVal string
// p := radix.Pipeline(
// radix.FlatCmd(nil, "SET", "foo", 1),
// radix.Cmd(&fooVal, "GET", "foo"),
// )
// if err := client.Do(p); err != nil {
// panic(err)
// }
// fmt.Printf("fooVal: %q\n", fooVal)
//
// Transactions
//
// There are two ways to perform transactions in redis. The first is with the
// MULTI/EXEC commands, which can be done using the WithConn Action (see its
// example). The second is using EVAL with lua scripting, which can be done
// using the EvalScript Action (again, see its example).
//
// EVAL with lua scripting is recommended in almost all cases. It only requires
// a single round-trip, it's infinitely more flexible than MULTI/EXEC, it's
// simpler to code, and for complex transactions, which would otherwise need a
// WATCH statement with MULTI/EXEC, it's significantly faster.
//
// AUTH and other settings via ConnFunc and ClientFunc
//
// All the client creation functions (e.g. NewPool) take in either a ConnFunc or
// a ClientFunc via their options. These can be used in order to set up timeouts
// on connections, perform authentication commands, or even implement custom
// pools.
//
// // this is a ConnFunc which will set up a connection which is authenticated
// // and has a 1 minute timeout on all operations
// customConnFunc := func(network, addr string) (radix.Conn, error) {
// return radix.Dial(network, addr,
// radix.DialTimeout(1 * time.Minute),
// radix.DialAuthPass("mySuperSecretPassword"),
// )
// }
//
// // this pool will use our ConnFunc for all connections it creates
// pool, err := radix.NewPool("tcp", redisAddr, 10, PoolConnFunc(customConnFunc))
//
// // this cluster will use the ClientFunc to create a pool to each node in the
// // cluster. The pools also use our customConnFunc, but have more connections
// poolFunc := func(network, addr string) (radix.Client, error) {
// return radix.NewPool(network, addr, 100, PoolConnFunc(customConnFunc))
// }
// cluster, err := radix.NewCluster([]string{redisAddr1, redisAddr2}, ClusterPoolFunc(poolFunc))
//
// Custom implementations
//
// All interfaces in this package were designed such that they could have custom
// implementations. There is no dependency within radix that demands any
// interface be implemented by a particular underlying type, so feel free to
// create your own Pools or Conns or Actions or whatever makes your life easier.
//
// Errors
//
// Errors returned from redis can be explicitly checked for using the the
// resp2.Error type. Note that the errors.As function, introduced in go 1.13,
// should be used.
//
// var redisErr resp2.Error
// err := client.Do(radix.Cmd(nil, "AUTH", "wrong password"))
// if errors.As(err, &redisErr) {
// log.Printf("redis error returned: %s", redisErr.E)
// }
//
// Use the golang.org/x/xerrors package if you're using an older version of go.
//
// Implicit pipelining
//
// Implicit pipelining is an optimization implemented and enabled in the default
// Pool implementation (and therefore also used by Cluster and Sentinel) which
// involves delaying concurrent Cmds and FlatCmds a small amount of time and
// sending them to redis in a single batch, similar to manually using a Pipeline.
// By doing this radix significantly reduces the I/O and CPU overhead for
// concurrent requests.
//
// Note that only commands which do not block are eligible for implicit pipelining.
//
// See the documentation on Pool for more information about the current
// implementation of implicit pipelining and for how to configure or disable
// the feature.
//
// For a performance comparisons between Clients with and without implicit
// pipelining see the benchmark results in the README.md.
//
package radix
import (
"errors"
)
var errClientClosed = errors.New("client is closed")
// Client describes an entity which can carry out Actions, e.g. a connection
// pool for a single redis instance or the cluster client.
//
// Implementations of Client are expected to be thread-safe, except in cases
// like Conn where they specify otherwise.
type Client interface {
// Do performs an Action, returning any error.
Do(Action) error
// Once Close() is called all future method calls on the Client will return
// an error
Close() error
}
// ClientFunc is a function which can be used to create a Client for a single
// redis instance on the given network/address.
type ClientFunc func(network, addr string) (Client, error)
// DefaultClientFunc is a ClientFunc which will return a Client for a redis
// instance using sane defaults.
var DefaultClientFunc = func(network, addr string) (Client, error) {
return NewPool(network, addr, 4)
}