-
-
Notifications
You must be signed in to change notification settings - Fork 35
/
doc.go
189 lines (189 loc) · 7.91 KB
/
doc.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
// Package redisc implements a redis cluster client on top of
// the redigo client package. It supports all commands that can
// be executed on a redis cluster, including pub-sub, scripts and
// read-only connections to read data from replicas.
// See http://redis.io/topics/cluster-spec for details.
//
// # Design
//
// The package defines two main types: Cluster and Conn. Both
// are described in more details below, but the Cluster manages
// the mapping of keys (or more exactly, hash slots computed from
// keys) to a group of nodes that form a redis cluster, and a
// Conn manages a connection to this cluster.
//
// The package is designed such that for simple uses, or when
// keys have been carefully named to play well with a redis
// cluster, a Cluster value can be used as a drop-in replacement
// for a redis.Pool from the redigo package.
//
// Similarly, the Conn type implements redigo's redis.Conn
// interface (and the augmented redis.ConnWithTimeout one too),
// so the API to execute commands is the same -
// in fact the redisc package uses the redigo package as its
// only third-party dependency.
//
// When more control is needed, the package offers some
// extra behaviour specific to working with a redis cluster:
//
// - Slot and SplitBySlot functions to compute the slot for
// a given key and to split a list of keys into groups of
// keys from the same slot, so that each group can safely be
// handled using the same connection.
//
// - *Conn.Bind (or the BindConn package-level helper function)
// to explicitly specify the keys that will be used with the
// connection so that the right node is selected, instead of
// relying on the automatic detection based on the first
// parameter of the command.
//
// - *Conn.ReadOnly (or the ReadOnlyConn package-level helper
// function) to mark a connection as read-only, allowing
// commands to be served by a replica instead of the master.
//
// - RetryConn to wrap a connection into one that automatically
// follows redirections when the cluster moves slots around.
//
// - Helper functions to deal with cluster-specific errors.
//
// # Cluster
//
// The Cluster type manages a redis cluster and offers an
// interface compatible with redigo's redis.Pool:
//
// Get() redis.Conn
// Close() error
//
// Along with some additional methods specific to a cluster:
//
// Dial() (redis.Conn, error)
// EachNode(bool, func(string, redis.Conn) error) error
// Refresh() error
// Stats() map[string]redis.PoolStats
//
// If the CreatePool function field is set, then a
// redis.Pool is created to manage connections to each of the
// cluster's nodes. A call to Get returns a connection
// from that pool.
//
// The Dial method, on the other hand, guarantees that
// the returned connection will not be managed by a pool, even if
// CreatePool is set. It calls redigo's redis.Dial function
// to create the unpooled connection, passing along any DialOptions
// set on the cluster. If the cluster's CreatePool field is nil,
// Get behaves the same as Dial.
//
// The Refresh method refreshes the cluster's internal mapping of
// hash slots to nodes. It should typically be called only once,
// after the cluster is created and before it is used, so that
// the first connections already benefit from smart routing.
// It is automatically kept up-to-date based on the redis MOVED
// responses afterwards.
//
// The EachNode method visits each node in the cluster and calls
// the provided function with a connection to that node, which may
// be useful to run diagnostics commands on each node or to collect
// keys across the whole cluster.
//
// The Stats method returns the pool statistics for each node, with
// the node's address as key of the map.
//
// A cluster must be closed once it is no longer used to release
// its resources.
//
// # Connection
//
// The connection returned from Get or Dial is a redigo redis.Conn
// interface (that also implements redis.ConnWithTimeout),
// with a concrete type of *Conn. In addition to the
// interface's required methods, *Conn adds the following methods:
//
// Bind(...string) error
// ReadOnly() error
//
// The returned connection is not yet connected to any node; it is
// "bound" to a specific node only when a call to Do, Send, Receive
// or Bind is made. For Do, Send and Receive, the node selection is
// implicit, it uses the first parameter of the command, and
// computes the hash slot assuming that first parameter is a key.
// It then binds the connection to the node corresponding to that
// slot. If there are no parameters for the command, or if there is
// no command (e.g. in a call to Receive), a random node is selected.
//
// Bind is explicit, it gives control to the caller over
// which node to select by specifying a list of keys that the caller
// wishes to handle with the connection. All keys must belong to the
// same slot, and the connection must not already be bound to a node,
// otherwise an error is returned. On success, the connection is
// bound to the node holding the slot of the specified key(s).
//
// Because the connection is returned as a redis.Conn interface, a
// type assertion must be used to access the underlying *Conn and
// to be able to call Bind:
//
// redisConn := cluster.Get()
// if conn, ok := redisConn.(*redisc.Conn); ok {
// if err := conn.Bind("my-key"); err != nil {
// // handle error
// }
// }
//
// The BindConn package-level function is provided as a helper for
// this common use-case.
//
// The ReadOnly method marks the connection as read-only, meaning that
// it will attempt to connect to a replica instead of the master node
// for its slot. Once bound to a node, the READONLY redis command is
// sent automatically, so it doesn't have to be sent explicitly before
// use. ReadOnly must be called before the connection is bound to a
// node, otherwise an error is returned.
//
// For the same reason as for Bind, a type assertion must be used to
// call ReadOnly on a *Conn, so a package-level helper function is
// also provided, ReadOnlyConn.
//
// There is no ReadWrite method, because it can be sent as a normal
// redis command and will essentially end that connection (all commands
// will now return MOVED errors). If the connection was wrapped in
// a RetryConn call, then it will automatically follow the redirection
// to the master node (see the Redirections section).
//
// The connection must be closed after use, to release the underlying
// resources.
//
// # Redirections
//
// The redis cluster may return MOVED and ASK errors when the node
// that received the command doesn't currently hold the slot corresponding
// to the key. The package cannot reliably handle those redirections
// automatically because the redirection error may be returned for
// a pipeline of commands, some of which may have succeeded.
//
// However, a connection can be wrapped by a call to RetryConn, which
// returns a redis.Conn interface where only calls to Do, Close and Err
// can succeed. That means pipelining is not supported, and only a single
// command can be executed at a time, but it will automatically handle
// MOVED and ASK replies, as well as TRYAGAIN errors.
//
// Note that even if RetryConn is not used, the cluster always updates
// its mapping of slots to nodes automatically by keeping track of
// MOVED replies.
//
// # Concurrency
//
// The concurrency model is similar to that of the redigo package:
//
// - Cluster methods are safe to call concurrently (like redis.Pool).
//
// - Connections do not support concurrent calls to write methods
// (Send, Flush) or concurrent calls to the read method (Receive).
//
// - Connections do allow a concurrent reader and writer.
//
// - Because the Do method combines the functionality of Send, Flush
// and Receive, it cannot be called concurrently with other methods.
//
// - The Bind and ReadOnly methods are safe to call concurrently, but
// there is not much point in doing so for as both will fail if
// the connection is already bound.
package redisc