gorsuch-redis 3.0.0.rc1

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