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 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-node 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 connection using
175
+
`#with`. You can pass a single key, in order to perform multiple operations atomically on the same key, like so:
176
+
177
+
```ruby
178
+
cli.with(key:'my_cool_key') do |conn|
179
+
conn.multi do |m|
180
+
m.call('INC', 'my_cool_key')
181
+
m.call('INC', 'my_cool_key')
182
+
end
183
+
# my_cool_key will be incremented by 2, with no intermediate state visible to other clients
184
+
end
185
+
```
186
+
187
+
More commonly, however, you will want to perform transactions across multiple keys. To do this, you need to ensure that all keys used in the transaction hash to the same slot; Redis a mechanism called [hashtags](https://redis.io/docs/reference/cluster-spec/#hash-tags) to achieve this. If a key contains a hashag (e.g. in the key `{foo}bar`, the hashtag is `foo`), then it is guaranted to hash to the same slot (and thus always live on the same node) as other keys which contain the same hashtag.
188
+
189
+
So, whilst it's not possible in Redis cluster to perform a transction on the keys `foo` and `bar`, it _is_ possible to perform a transaction on the keys `{tag}foo` and `{tag}bar`. To perform such transactions on this gem, pass `hashtag:` to `#with` instead of `key`:
190
+
191
+
```ruby
192
+
cli.with(hashtag:'user123') do |conn|
193
+
# You can use any key which contains "{user123}" in this block
194
+
conn.multi do |m|
195
+
m.call('INC', '{user123}coins_spent')
196
+
m.call('DEC', '{user123}coins_available')
197
+
end
198
+
end
199
+
```
200
+
201
+
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
237
+
you may have written non-idempotent code inside your block, the block is not automatically retried if e.g. the slot
238
+
it is operating on moves to a different node. If you want this, you can opt-in to retries by passing nonzero
239
+
`retry_count` to `#with`.
240
+
```ruby
241
+
cli.with(hashtag:'myslot', retry_count:1) do |conn|
242
+
conn.call('GET', '{myslot}1')
243
+
#=> "value1"
244
+
# Now, some changes in cluster topology mean that {key} is moved to a different node!
245
+
conn.call('GET', '{myslot}2')
246
+
#=> MOVED 9039 127.0.0.1:16381 (RedisClient::CommandError)
247
+
# Luckily, the block will get retried (once) and so both GETs will be re-executed on the newly-discovered
248
+
# correct node.
249
+
end
250
+
```
251
+
252
+
Because `RedisClient` from the redis-client gem implements `#with` as simply `yield self` and ignores all of its
253
+
arguments, it's possible to write code which is compatible with both redis-client and redis-cluster-client; the `#with`
254
+
call will pin the connection to a slot when using clustering, or be a no-op when not.
255
+
171
256
## ACL
172
257
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