yam-redis-with-retries 2.2.2.1

Sign up to get free protection for your applications and to get access to all the features.
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