gorsuch-redis 3.0.0.rc1

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