You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This implements support for "pinning" a cluster client to a particular
keyslot. This allows the use of WATCH/MULTI/EXEC transactions on that
node without any ambiguity.
Because RedisClient also implements watch as "yield self" and ignores
all arguments, this means that you can easily write transactions that
are compatible with both cluster and non-clustered redis (provided you
use hashtags for your keys).
This gem supports [Redis transactions](https://redis.io/topics/transactions), including atomicity with `MULTI`/`EXEC`,
173
+
and conditional execution with `WATCH`. Redis does not support cross-slot transactions, so all keys used within a
174
+
transaction must live in the same key slot. To use transactions, you must thus "pin" your client to a single slot using
175
+
`#with`. Pass a key to `#with`, and the yielded connection object will enforce that all keys used in the block must
176
+
belong to the same slot.
177
+
178
+
```ruby
179
+
cli.with(key:'{key}1') do |conn|
180
+
conn.call('SET', '{key}1', 'This is OK')
181
+
#=> "OK"
182
+
conn.call('SET', '{key}2', 'This is also OK; it has the same hashtag, and so the same slot')
183
+
#=> "OK"
184
+
conn.call('SET', '{otherslot}3', 'This will raise an exception; this key is not in the same slot as {key}1')
185
+
#=> Connection pinned to slot 12539 but command ["SET", "{otherslot}3", "..."] includes key {otherslot}3 in slot 10271 (RedisClient::Cluster::Transaction::ConsistencyError)
186
+
end
187
+
```
188
+
189
+
When using hash tags to enforce same-slot key hashing, it's often neat to simply pass the hashtag _only_ to `#with`:
190
+
191
+
```ruby
192
+
cli.with(key:'{myslot}') do |conn|
193
+
# You can use any key starting with {myslot} in this block
194
+
end
195
+
```
196
+
197
+
Once you have pinned a client to a particular slot, you can use the same transaction APIs as the
Pinned connections are aware of redirections and node failures like ordinary calls to `RedisClient::Cluster`, but because
236
+
you may have written non-idempotent code inside your block, the block is not automatically retried if e.g. the slot
237
+
it is operating on moves to a different node. If you want this, you can opt-in to retries by passing nonzero
238
+
`retry_count` to `#with`.
239
+
240
+
```ruby
241
+
cli.with(key:'{key}', retry_count:1) do |conn|
242
+
conn.call('GET', '{key}1')
243
+
#=> "value1"
244
+
245
+
# Now, some changes in cluster topology mean that {key} is moved to a different node!
246
+
247
+
conn.call('GET', '{key}2')
248
+
#=> MOVED 9039 127.0.0.1:16381 (RedisClient::CommandError)
249
+
250
+
# Luckily, the block will get retried (once) and so both GETs will be re-executed on the newly-discovered
251
+
# correct node.
252
+
end
253
+
```
254
+
255
+
Because `RedisClient` from the redis-client gem implements `#with` as simply `yield self` and ignores all of its
256
+
arguments, it's possible to write code which is compatible with both redis-client and redis-cluster-client; the `#with`
257
+
call will pin the connection to a slot when using clustering, or be a no-op when not.
258
+
171
259
## ACL
172
260
The cluster client internally calls [COMMAND](https://redis.io/commands/command/) and [CLUSTER NODES](https://redis.io/commands/cluster-nodes/) commands to operate correctly.
0 commit comments