yam-redis-with-retries 2.2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. data/CHANGELOG.md +53 -0
  2. data/LICENSE +20 -0
  3. data/README.md +208 -0
  4. data/Rakefile +277 -0
  5. data/benchmarking/logging.rb +62 -0
  6. data/benchmarking/pipeline.rb +51 -0
  7. data/benchmarking/speed.rb +21 -0
  8. data/benchmarking/suite.rb +24 -0
  9. data/benchmarking/thread_safety.rb +38 -0
  10. data/benchmarking/worker.rb +71 -0
  11. data/examples/basic.rb +15 -0
  12. data/examples/dist_redis.rb +43 -0
  13. data/examples/incr-decr.rb +17 -0
  14. data/examples/list.rb +26 -0
  15. data/examples/pubsub.rb +31 -0
  16. data/examples/sets.rb +36 -0
  17. data/examples/unicorn/config.ru +3 -0
  18. data/examples/unicorn/unicorn.rb +20 -0
  19. data/lib/redis.rb +1166 -0
  20. data/lib/redis/client.rb +265 -0
  21. data/lib/redis/compat.rb +21 -0
  22. data/lib/redis/connection.rb +9 -0
  23. data/lib/redis/connection/command_helper.rb +45 -0
  24. data/lib/redis/connection/hiredis.rb +49 -0
  25. data/lib/redis/connection/registry.rb +12 -0
  26. data/lib/redis/connection/ruby.rb +135 -0
  27. data/lib/redis/connection/synchrony.rb +129 -0
  28. data/lib/redis/distributed.rb +694 -0
  29. data/lib/redis/hash_ring.rb +131 -0
  30. data/lib/redis/pipeline.rb +34 -0
  31. data/lib/redis/retry.rb +128 -0
  32. data/lib/redis/subscribe.rb +94 -0
  33. data/lib/redis/version.rb +3 -0
  34. data/test/commands_on_hashes_test.rb +20 -0
  35. data/test/commands_on_lists_test.rb +60 -0
  36. data/test/commands_on_sets_test.rb +78 -0
  37. data/test/commands_on_sorted_sets_test.rb +109 -0
  38. data/test/commands_on_strings_test.rb +80 -0
  39. data/test/commands_on_value_types_test.rb +88 -0
  40. data/test/connection_handling_test.rb +88 -0
  41. data/test/distributed_blocking_commands_test.rb +53 -0
  42. data/test/distributed_commands_on_hashes_test.rb +12 -0
  43. data/test/distributed_commands_on_lists_test.rb +24 -0
  44. data/test/distributed_commands_on_sets_test.rb +85 -0
  45. data/test/distributed_commands_on_strings_test.rb +50 -0
  46. data/test/distributed_commands_on_value_types_test.rb +73 -0
  47. data/test/distributed_commands_requiring_clustering_test.rb +148 -0
  48. data/test/distributed_connection_handling_test.rb +25 -0
  49. data/test/distributed_internals_test.rb +27 -0
  50. data/test/distributed_key_tags_test.rb +53 -0
  51. data/test/distributed_persistence_control_commands_test.rb +24 -0
  52. data/test/distributed_publish_subscribe_test.rb +101 -0
  53. data/test/distributed_remote_server_control_commands_test.rb +43 -0
  54. data/test/distributed_sorting_test.rb +21 -0
  55. data/test/distributed_test.rb +59 -0
  56. data/test/distributed_transactions_test.rb +34 -0
  57. data/test/encoding_test.rb +16 -0
  58. data/test/error_replies_test.rb +53 -0
  59. data/test/helper.rb +145 -0
  60. data/test/internals_test.rb +163 -0
  61. data/test/lint/hashes.rb +126 -0
  62. data/test/lint/internals.rb +37 -0
  63. data/test/lint/lists.rb +93 -0
  64. data/test/lint/sets.rb +66 -0
  65. data/test/lint/sorted_sets.rb +167 -0
  66. data/test/lint/strings.rb +137 -0
  67. data/test/lint/value_types.rb +84 -0
  68. data/test/persistence_control_commands_test.rb +22 -0
  69. data/test/pipelining_commands_test.rb +123 -0
  70. data/test/publish_subscribe_test.rb +158 -0
  71. data/test/redis_mock.rb +80 -0
  72. data/test/remote_server_control_commands_test.rb +82 -0
  73. data/test/retry_test.rb +225 -0
  74. data/test/sorting_test.rb +44 -0
  75. data/test/synchrony_driver.rb +57 -0
  76. data/test/test.conf +8 -0
  77. data/test/thread_safety_test.rb +30 -0
  78. data/test/transactions_test.rb +100 -0
  79. data/test/unknown_commands_test.rb +14 -0
  80. data/test/url_param_test.rb +60 -0
  81. metadata +215 -0
@@ -0,0 +1,129 @@
1
+ require "redis/connection/command_helper"
2
+ require "redis/connection/registry"
3
+ require "em-synchrony"
4
+ require "hiredis/reader"
5
+
6
+ class Redis
7
+ module Connection
8
+ class RedisClient < EventMachine::Connection
9
+ include EventMachine::Deferrable
10
+
11
+ def post_init
12
+ @req = nil
13
+ @connected = false
14
+ @reader = ::Hiredis::Reader.new
15
+ end
16
+
17
+ def connection_completed
18
+ @connected = true
19
+ succeed
20
+ end
21
+
22
+ def connected?
23
+ @connected
24
+ end
25
+
26
+ def receive_data(data)
27
+ @reader.feed(data)
28
+
29
+ begin
30
+ until (reply = @reader.gets) == false
31
+ @req.succeed [:reply, reply]
32
+ end
33
+ rescue RuntimeError => err
34
+ @req.fail [:error, ::Redis::ProtocolError.new(err.message)]
35
+ end
36
+ end
37
+
38
+ def read
39
+ @req = EventMachine::DefaultDeferrable.new
40
+ EventMachine::Synchrony.sync @req
41
+ end
42
+
43
+ def send(data)
44
+ callback { send_data data }
45
+ end
46
+
47
+ def unbind
48
+ @connected = false
49
+ if @req
50
+ @req.fail [:error, Errno::ECONNRESET]
51
+ @req = nil
52
+ else
53
+ fail
54
+ end
55
+ end
56
+ end
57
+
58
+ class Synchrony
59
+ include Redis::Connection::CommandHelper
60
+
61
+ def initialize
62
+ @timeout = 5_000_000
63
+ @connection = nil
64
+ end
65
+
66
+ def connected?
67
+ @connection && @connection.connected?
68
+ end
69
+
70
+ def timeout=(usecs)
71
+ @timeout = usecs
72
+ end
73
+
74
+ def connect(host, port, timeout)
75
+ conn = EventMachine.connect(host, port, RedisClient) do |c|
76
+ c.pending_connect_timeout = [Float(timeout / 1_000_000), 0.1].max
77
+ end
78
+
79
+ setup_connect_callbacks(conn, Fiber.current)
80
+ end
81
+
82
+ def connect_unix(path, timeout)
83
+ conn = EventMachine.connect_unix_domain(path, RedisClient)
84
+ setup_connect_callbacks(conn, Fiber.current)
85
+ end
86
+
87
+ def disconnect
88
+ @connection.close_connection
89
+ @connection = nil
90
+ end
91
+
92
+ def write(command)
93
+ @connection.send(build_command(command))
94
+ end
95
+
96
+ def read
97
+ type, payload = @connection.read
98
+
99
+ if type == :reply
100
+ payload
101
+ elsif type == :error
102
+ raise payload
103
+ else
104
+ raise "Unknown type #{type.inspect}"
105
+ end
106
+ end
107
+
108
+ private
109
+
110
+ def setup_connect_callbacks(conn, f)
111
+ conn.callback do
112
+ @connection = conn
113
+ f.resume conn
114
+ end
115
+
116
+ conn.errback do
117
+ @connection = conn
118
+ f.resume :refused
119
+ end
120
+
121
+ r = Fiber.yield
122
+ raise Errno::ECONNREFUSED if r == :refused
123
+ r
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ Redis::Connection.drivers << Redis::Connection::Synchrony
@@ -0,0 +1,694 @@
1
+ require "redis/hash_ring"
2
+
3
+ class Redis
4
+ class Distributed
5
+
6
+ class CannotDistribute < RuntimeError
7
+ def initialize(command)
8
+ @command = command
9
+ end
10
+
11
+ def message
12
+ "#{@command.to_s.upcase} cannot be used in Redis::Distributed because the keys involved need to be on the same server or because we cannot guarantee that the operation will be atomic."
13
+ end
14
+ end
15
+
16
+ attr_reader :ring
17
+
18
+ def initialize(urls, options={})
19
+ @tag = options.delete(:tag) || /^\{(.+?)\}/
20
+ @default_options = options
21
+ @ring = HashRing.new urls.map { |url| Redis.connect(options.merge(:url => url)) }
22
+ @subscribed_node = nil
23
+ end
24
+
25
+ def node_for(key)
26
+ @ring.get_node(key_tag(key.to_s) || key.to_s)
27
+ end
28
+
29
+ def nodes
30
+ @ring.nodes
31
+ end
32
+
33
+ def add_node(url)
34
+ @ring.add_node Redis.connect(@default_options.merge(:url => url))
35
+ end
36
+
37
+ # Close the connection.
38
+ def quit
39
+ on_each_node :quit
40
+ end
41
+
42
+ # Change the selected database for the current connection.
43
+ def select(db)
44
+ on_each_node :select, db
45
+ end
46
+
47
+ # Ping the server.
48
+ def ping
49
+ on_each_node :ping
50
+ end
51
+
52
+ # Remove all keys from all databases.
53
+ def flushall
54
+ on_each_node :flushall
55
+ end
56
+
57
+ # Determine if a key exists.
58
+ def exists(key)
59
+ node_for(key).exists(key)
60
+ end
61
+
62
+ # Delete a key.
63
+ def del(*args)
64
+ keys_per_node = args.group_by { |key| node_for(key) }
65
+ keys_per_node.inject(0) do |sum, (node, keys)|
66
+ sum + node.del(*keys)
67
+ end
68
+ end
69
+
70
+ # Determine the type stored at key.
71
+ def type(key)
72
+ node_for(key).type(key)
73
+ end
74
+
75
+ # Find all keys matching the given pattern.
76
+ def keys(glob = "*")
77
+ on_each_node(:keys, glob).flatten
78
+ end
79
+
80
+ # Return a random key from the keyspace.
81
+ def randomkey
82
+ raise CannotDistribute, :randomkey
83
+ end
84
+
85
+ # Rename a key.
86
+ def rename(old_name, new_name)
87
+ ensure_same_node(:rename, old_name, new_name) do |node|
88
+ node.rename(old_name, new_name)
89
+ end
90
+ end
91
+
92
+ # Rename a key, only if the new key does not exist.
93
+ def renamenx(old_name, new_name)
94
+ ensure_same_node(:renamenx, old_name, new_name) do |node|
95
+ node.renamenx(old_name, new_name)
96
+ end
97
+ end
98
+
99
+ # Return the number of keys in the selected database.
100
+ def dbsize
101
+ on_each_node :dbsize
102
+ end
103
+
104
+ # Set a key's time to live in seconds.
105
+ def expire(key, seconds)
106
+ node_for(key).expire(key, seconds)
107
+ end
108
+
109
+ # Set the expiration for a key as a UNIX timestamp.
110
+ def expireat(key, unix_time)
111
+ node_for(key).expireat(key, unix_time)
112
+ end
113
+
114
+ # Remove the expiration from a key.
115
+ def persist(key)
116
+ node_for(key).persist(key)
117
+ end
118
+
119
+ # Get the time to live for a key.
120
+ def ttl(key)
121
+ node_for(key).ttl(key)
122
+ end
123
+
124
+ # Move a key to another database.
125
+ def move(key, db)
126
+ node_for(key).move(key, db)
127
+ end
128
+
129
+ # Remove all keys from the current database.
130
+ def flushdb
131
+ on_each_node :flushdb
132
+ end
133
+
134
+ # Set the string value of a key.
135
+ def set(key, value)
136
+ node_for(key).set(key, value)
137
+ end
138
+
139
+ # Sets or clears the bit at offset in the string value stored at key.
140
+ def setbit(key, offset, value)
141
+ node_for(key).setbit(key, offset, value)
142
+ end
143
+
144
+ # Overwrite part of a string at key starting at the specified offset.
145
+ def setrange(key, offset, value)
146
+ node_for(key).setrange(key, offset, value)
147
+ end
148
+
149
+ # Set the value and expiration of a key.
150
+ def setex(key, ttl, value)
151
+ node_for(key).setex(key, ttl, value)
152
+ end
153
+
154
+ # Get the value of a key.
155
+ def get(key)
156
+ node_for(key).get(key)
157
+ end
158
+
159
+ # Returns the bit value at offset in the string value stored at key.
160
+ def getbit(key, offset)
161
+ node_for(key).getbit(key, offset)
162
+ end
163
+
164
+ # Get a substring of the string stored at a key.
165
+ def getrange(key, start, stop)
166
+ node_for(key).getrange(key, start, stop)
167
+ end
168
+
169
+ # Set the string value of a key and return its old value.
170
+ def getset(key, value)
171
+ node_for(key).getset(key, value)
172
+ end
173
+
174
+ def [](key)
175
+ get(key)
176
+ end
177
+
178
+ # Append a value to a key.
179
+ def append(key, value)
180
+ node_for(key).append(key, value)
181
+ end
182
+
183
+ def substr(key, start, stop)
184
+ node_for(key).substr(key, start, stop)
185
+ end
186
+
187
+ def []=(key,value)
188
+ set(key, value)
189
+ end
190
+
191
+ # Get the values of all the given keys.
192
+ def mget(*keys)
193
+ raise CannotDistribute, :mget
194
+ end
195
+
196
+ def mapped_mget(*keys)
197
+ raise CannotDistribute, :mapped_mget
198
+ end
199
+
200
+ # Set the value of a key, only if the key does not exist.
201
+ def setnx(key, value)
202
+ node_for(key).setnx(key, value)
203
+ end
204
+
205
+ # Set multiple keys to multiple values.
206
+ def mset(*args)
207
+ raise CannotDistribute, :mset
208
+ end
209
+
210
+ def mapped_mset(hash)
211
+ mset(*hash.to_a.flatten)
212
+ end
213
+
214
+ # Set multiple keys to multiple values, only if none of the keys exist.
215
+ def msetnx(*args)
216
+ raise CannotDistribute, :msetnx
217
+ end
218
+
219
+ def mapped_msetnx(hash)
220
+ raise CannotDistribute, :mapped_msetnx
221
+ end
222
+
223
+ # Increment the integer value of a key by one.
224
+ def incr(key)
225
+ node_for(key).incr(key)
226
+ end
227
+
228
+ # Increment the integer value of a key by the given number.
229
+ def incrby(key, increment)
230
+ node_for(key).incrby(key, increment)
231
+ end
232
+
233
+ # Decrement the integer value of a key by one.
234
+ def decr(key)
235
+ node_for(key).decr(key)
236
+ end
237
+
238
+ # Decrement the integer value of a key by the given number.
239
+ def decrby(key, decrement)
240
+ node_for(key).decrby(key, decrement)
241
+ end
242
+
243
+ # Append a value to a list.
244
+ def rpush(key, value)
245
+ node_for(key).rpush(key, value)
246
+ end
247
+
248
+ # Prepend a value to a list.
249
+ def lpush(key, value)
250
+ node_for(key).lpush(key, value)
251
+ end
252
+
253
+ # Get the length of a list.
254
+ def llen(key)
255
+ node_for(key).llen(key)
256
+ end
257
+
258
+ # Get a range of elements from a list.
259
+ def lrange(key, start, stop)
260
+ node_for(key).lrange(key, start, stop)
261
+ end
262
+
263
+ # Trim a list to the specified range.
264
+ def ltrim(key, start, stop)
265
+ node_for(key).ltrim(key, start, stop)
266
+ end
267
+
268
+ # Get an element from a list by its index.
269
+ def lindex(key, index)
270
+ node_for(key).lindex(key, index)
271
+ end
272
+
273
+ # Set the value of an element in a list by its index.
274
+ def lset(key, index, value)
275
+ node_for(key).lset(key, index, value)
276
+ end
277
+
278
+ # Remove elements from a list.
279
+ def lrem(key, count, value)
280
+ node_for(key).lrem(key, count, value)
281
+ end
282
+
283
+ # Remove and get the first element in a list.
284
+ def lpop(key)
285
+ node_for(key).lpop(key)
286
+ end
287
+
288
+ # Remove and get the last element in a list.
289
+ def rpop(key)
290
+ node_for(key).rpop(key)
291
+ end
292
+
293
+ # Remove the last element in a list, append it to another list and return
294
+ # it.
295
+ def rpoplpush(source, destination)
296
+ ensure_same_node(:rpoplpush, source, destination) do |node|
297
+ node.rpoplpush(source, destination)
298
+ end
299
+ end
300
+
301
+ # Remove and get the first element in a list, or block until one is
302
+ # available.
303
+ def blpop(key, timeout)
304
+ node_for(key).blpop(key, timeout)
305
+ end
306
+
307
+ # Remove and get the last element in a list, or block until one is
308
+ # available.
309
+ def brpop(key, timeout)
310
+ node_for(key).brpop(key, timeout)
311
+ end
312
+
313
+ # Pop a value from a list, push it to another list and return it; or block
314
+ # until one is available.
315
+ def brpoplpush(source, destination, timeout)
316
+ ensure_same_node(:brpoplpush, source, destination) do |node|
317
+ node.brpoplpush(source, destination, timeout)
318
+ end
319
+ end
320
+
321
+ # Add a member to a set.
322
+ def sadd(key, value)
323
+ node_for(key).sadd(key, value)
324
+ end
325
+
326
+ # Remove a member from a set.
327
+ def srem(key, value)
328
+ node_for(key).srem(key, value)
329
+ end
330
+
331
+ # Remove and return a random member from a set.
332
+ def spop(key)
333
+ node_for(key).spop(key)
334
+ end
335
+
336
+ # Move a member from one set to another.
337
+ def smove(source, destination, member)
338
+ ensure_same_node(:smove, source, destination) do |node|
339
+ node.smove(source, destination, member)
340
+ end
341
+ end
342
+
343
+ # Get the number of members in a set.
344
+ def scard(key)
345
+ node_for(key).scard(key)
346
+ end
347
+
348
+ # Determine if a given value is a member of a set.
349
+ def sismember(key, member)
350
+ node_for(key).sismember(key, member)
351
+ end
352
+
353
+ # Intersect multiple sets.
354
+ def sinter(*keys)
355
+ ensure_same_node(:sinter, *keys) do |node|
356
+ node.sinter(*keys)
357
+ end
358
+ end
359
+
360
+ # Intersect multiple sets and store the resulting set in a key.
361
+ def sinterstore(destination, *keys)
362
+ ensure_same_node(:sinterstore, destination, *keys) do |node|
363
+ node.sinterstore(destination, *keys)
364
+ end
365
+ end
366
+
367
+ # Add multiple sets.
368
+ def sunion(*keys)
369
+ ensure_same_node(:sunion, *keys) do |node|
370
+ node.sunion(*keys)
371
+ end
372
+ end
373
+
374
+ # Add multiple sets and store the resulting set in a key.
375
+ def sunionstore(destination, *keys)
376
+ ensure_same_node(:sunionstore, destination, *keys) do |node|
377
+ node.sunionstore(destination, *keys)
378
+ end
379
+ end
380
+
381
+ # Subtract multiple sets.
382
+ def sdiff(*keys)
383
+ ensure_same_node(:sdiff, *keys) do |node|
384
+ node.sdiff(*keys)
385
+ end
386
+ end
387
+
388
+ # Subtract multiple sets and store the resulting set in a key.
389
+ def sdiffstore(destination, *keys)
390
+ ensure_same_node(:sdiffstore, destination, *keys) do |node|
391
+ node.sdiffstore(destination, *keys)
392
+ end
393
+ end
394
+
395
+ # Get all the members in a set.
396
+ def smembers(key)
397
+ node_for(key).smembers(key)
398
+ end
399
+
400
+ # Get a random member from a set.
401
+ def srandmember(key)
402
+ node_for(key).srandmember(key)
403
+ end
404
+
405
+ # Add a member to a sorted set, or update its score if it already exists.
406
+ def zadd(key, score, member)
407
+ node_for(key).zadd(key, score, member)
408
+ end
409
+
410
+ # Remove a member from a sorted set.
411
+ def zrem(key, member)
412
+ node_for(key).zrem(key, member)
413
+ end
414
+
415
+ # Increment the score of a member in a sorted set.
416
+ def zincrby(key, increment, member)
417
+ node_for(key).zincrby(key, increment, member)
418
+ end
419
+
420
+ # Return a range of members in a sorted set, by index.
421
+ def zrange(key, start, stop, options = {})
422
+ node_for(key).zrange(key, start, stop, options)
423
+ end
424
+
425
+ # Determine the index of a member in a sorted set.
426
+ def zrank(key, member)
427
+ node_for(key).zrank(key, member)
428
+ end
429
+
430
+ # Determine the index of a member in a sorted set, with scores ordered from
431
+ # high to low.
432
+ def zrevrank(key, member)
433
+ node_for(key).zrevrank(key, member)
434
+ end
435
+
436
+ # Return a range of members in a sorted set, by index, with scores ordered
437
+ # from high to low.
438
+ def zrevrange(key, start, stop, options = {})
439
+ node_for(key).zrevrange(key, start, stop, options)
440
+ end
441
+
442
+ # Remove all members in a sorted set within the given scores.
443
+ def zremrangebyscore(key, min, max)
444
+ node_for(key).zremrangebyscore(key, min, max)
445
+ end
446
+
447
+ # Remove all members in a sorted set within the given indexes.
448
+ def zremrangebyrank(key, start, stop)
449
+ node_for(key).zremrangebyrank(key, start, stop)
450
+ end
451
+
452
+ # Return a range of members in a sorted set, by score.
453
+ def zrangebyscore(key, min, max, options = {})
454
+ node_for(key).zrangebyscore(key, min, max, options)
455
+ end
456
+
457
+ # Return a range of members in a sorted set, by score, with scores ordered
458
+ # from high to low.
459
+ def zrevrangebyscore(key, max, min, options = {})
460
+ node_for(key).zrevrangebyscore(key, max, min, options)
461
+ end
462
+
463
+ # Get the number of members in a sorted set.
464
+ def zcard(key)
465
+ node_for(key).zcard(key)
466
+ end
467
+
468
+ # Get the score associated with the given member in a sorted set.
469
+ def zscore(key, member)
470
+ node_for(key).zscore(key, member)
471
+ end
472
+
473
+ # Intersect multiple sorted sets and store the resulting sorted set in a new
474
+ # key.
475
+ def zinterstore(destination, keys, options = {})
476
+ ensure_same_node(:zinterstore, destination, *keys) do |node|
477
+ node.zinterstore(destination, keys, options)
478
+ end
479
+ end
480
+
481
+ # Add multiple sorted sets and store the resulting sorted set in a new key.
482
+ def zunionstore(destination, keys, options = {})
483
+ ensure_same_node(:zunionstore, destination, *keys) do |node|
484
+ node.zunionstore(destination, keys, options)
485
+ end
486
+ end
487
+
488
+ # Set the string value of a hash field.
489
+ def hset(key, field, value)
490
+ node_for(key).hset(key, field, value)
491
+ end
492
+
493
+ # Set the value of a hash field, only if the field does not exist.
494
+ def hsetnx(key, field, value)
495
+ node_for(key).hsetnx(key, field, value)
496
+ end
497
+
498
+ # Get the value of a hash field.
499
+ def hget(key, field)
500
+ node_for(key).hget(key, field)
501
+ end
502
+
503
+ # Delete a hash field.
504
+ def hdel(key, field)
505
+ node_for(key).hdel(key, field)
506
+ end
507
+
508
+ # Determine if a hash field exists.
509
+ def hexists(key, field)
510
+ node_for(key).hexists(key, field)
511
+ end
512
+
513
+ # Get the number of fields in a hash.
514
+ def hlen(key)
515
+ node_for(key).hlen(key)
516
+ end
517
+
518
+ # Get all the fields in a hash.
519
+ def hkeys(key)
520
+ node_for(key).hkeys(key)
521
+ end
522
+
523
+ # Get all the values in a hash.
524
+ def hvals(key)
525
+ node_for(key).hvals(key)
526
+ end
527
+
528
+ # Get all the fields and values in a hash.
529
+ def hgetall(key)
530
+ node_for(key).hgetall(key)
531
+ end
532
+
533
+ # Set multiple hash fields to multiple values.
534
+ def hmset(key, *attrs)
535
+ node_for(key).hmset(key, *attrs)
536
+ end
537
+
538
+ def mapped_hmset(key, hash)
539
+ node_for(key).hmset(key, *hash.to_a.flatten)
540
+ end
541
+
542
+ # Get the values of all the given hash fields.
543
+ def hmget(key, *fields)
544
+ node_for(key).hmget(key, *fields)
545
+ end
546
+
547
+ def mapped_hmget(key, *fields)
548
+ Hash[*fields.zip(hmget(key, *fields)).flatten]
549
+ end
550
+
551
+ # Increment the integer value of a hash field by the given number.
552
+ def hincrby(key, field, increment)
553
+ node_for(key).hincrby(key, field, increment)
554
+ end
555
+
556
+ # Sort the elements in a list, set or sorted set.
557
+ def sort(key, options = {})
558
+ keys = [key, options[:by], options[:store], *Array(options[:get])].compact
559
+ keys.delete('#')
560
+ ensure_same_node(:sort, *keys) do |node|
561
+ node.sort(key, options)
562
+ end
563
+ end
564
+
565
+ # Mark the start of a transaction block.
566
+ def multi
567
+ raise CannotDistribute, :multi
568
+ end
569
+
570
+ # Watch the given keys to determine execution of the MULTI/EXEC block.
571
+ def watch(*keys)
572
+ raise CannotDistribute, :watch
573
+ end
574
+
575
+ # Forget about all watched keys.
576
+ def unwatch
577
+ raise CannotDistribute, :unwatch
578
+ end
579
+
580
+ # Execute all commands issued after MULTI.
581
+ def exec
582
+ raise CannotDistribute, :exec
583
+ end
584
+
585
+ # Discard all commands issued after MULTI.
586
+ def discard
587
+ raise CannotDistribute, :discard
588
+ end
589
+
590
+ # Post a message to a channel.
591
+ def publish(channel, message)
592
+ node_for(channel).publish(channel, message)
593
+ end
594
+
595
+ def subscribed?
596
+ !! @subscribed_node
597
+ end
598
+
599
+ # Stop listening for messages posted to the given channels.
600
+ def unsubscribe(*channels)
601
+ raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
602
+ @subscribed_node.unsubscribe(*channels)
603
+ end
604
+
605
+ # Listen for messages published to the given channels.
606
+ def subscribe(channel, *channels, &block)
607
+ if channels.empty?
608
+ @subscribed_node = node_for(channel)
609
+ @subscribed_node.subscribe(channel, &block)
610
+ else
611
+ ensure_same_node(:subscribe, channel, *channels) do |node|
612
+ @subscribed_node = node
613
+ node.subscribe(channel, *channels, &block)
614
+ end
615
+ end
616
+ end
617
+
618
+ # Stop listening for messages posted to channels matching the given
619
+ # patterns.
620
+ def punsubscribe(*channels)
621
+ raise NotImplementedError
622
+ end
623
+
624
+ # Listen for messages published to channels matching the given patterns.
625
+ def psubscribe(*channels, &block)
626
+ raise NotImplementedError
627
+ end
628
+
629
+ # Synchronously save the dataset to disk.
630
+ def save
631
+ on_each_node :save
632
+ end
633
+
634
+ # Asynchronously save the dataset to disk.
635
+ def bgsave
636
+ on_each_node :bgsave
637
+ end
638
+
639
+ # Get the UNIX time stamp of the last successful save to disk.
640
+ def lastsave
641
+ on_each_node :lastsave
642
+ end
643
+
644
+ # Get information and statistics about the server.
645
+ def info(cmd = nil)
646
+ on_each_node :info, cmd
647
+ end
648
+
649
+ # Listen for all requests received by the server in real time.
650
+ def monitor
651
+ raise NotImplementedError
652
+ end
653
+
654
+ # Echo the given string.
655
+ def echo(value)
656
+ on_each_node :echo, value
657
+ end
658
+
659
+ def pipelined
660
+ raise CannotDistribute, :pipelined
661
+ end
662
+
663
+ def inspect
664
+ node_info = nodes.map do |node|
665
+ "#{node.id} (Redis v#{node.info['redis_version']})"
666
+ end
667
+ "#<Redis client v#{Redis::VERSION} connected to #{node_info.join(', ')}>"
668
+ end
669
+
670
+ protected
671
+
672
+ def on_each_node(command, *args)
673
+ nodes.map do |node|
674
+ node.send(command, *args)
675
+ end
676
+ end
677
+
678
+ def node_index_for(key)
679
+ nodes.index(node_for(key))
680
+ end
681
+
682
+ def key_tag(key)
683
+ key.to_s[@tag, 1] if @tag
684
+ end
685
+
686
+ def ensure_same_node(command, *keys)
687
+ tags = keys.map { |key| key_tag(key) }
688
+
689
+ raise CannotDistribute, command if !tags.all? || tags.uniq.size != 1
690
+
691
+ yield(node_for(keys.first))
692
+ end
693
+ end
694
+ end