redis 3.0.0 → 4.5.0
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +315 -0
- data/README.md +301 -58
- data/lib/redis/client.rb +383 -88
- data/lib/redis/cluster/command.rb +81 -0
- data/lib/redis/cluster/command_loader.rb +33 -0
- data/lib/redis/cluster/key_slot_converter.rb +72 -0
- data/lib/redis/cluster/node.rb +108 -0
- data/lib/redis/cluster/node_key.rb +31 -0
- data/lib/redis/cluster/node_loader.rb +37 -0
- data/lib/redis/cluster/option.rb +93 -0
- data/lib/redis/cluster/slot.rb +86 -0
- data/lib/redis/cluster/slot_loader.rb +49 -0
- data/lib/redis/cluster.rb +291 -0
- data/lib/redis/connection/command_helper.rb +7 -10
- data/lib/redis/connection/hiredis.rb +12 -8
- data/lib/redis/connection/registry.rb +2 -1
- data/lib/redis/connection/ruby.rb +266 -74
- data/lib/redis/connection/synchrony.rb +41 -14
- data/lib/redis/connection.rb +4 -2
- data/lib/redis/distributed.rb +258 -76
- data/lib/redis/errors.rb +48 -0
- data/lib/redis/hash_ring.rb +31 -73
- data/lib/redis/pipeline.rb +74 -18
- data/lib/redis/subscribe.rb +24 -13
- data/lib/redis/version.rb +3 -1
- data/lib/redis.rb +2068 -464
- metadata +63 -160
- data/.gitignore +0 -10
- data/.order +0 -169
- data/.travis/Gemfile +0 -11
- data/.travis.yml +0 -50
- data/.yardopts +0 -3
- data/Rakefile +0 -392
- data/benchmarking/logging.rb +0 -62
- data/benchmarking/pipeline.rb +0 -51
- data/benchmarking/speed.rb +0 -21
- data/benchmarking/suite.rb +0 -24
- data/benchmarking/worker.rb +0 -71
- data/examples/basic.rb +0 -15
- data/examples/dist_redis.rb +0 -43
- data/examples/incr-decr.rb +0 -17
- data/examples/list.rb +0 -26
- data/examples/pubsub.rb +0 -31
- data/examples/sets.rb +0 -36
- data/examples/unicorn/config.ru +0 -3
- data/examples/unicorn/unicorn.rb +0 -20
- data/redis.gemspec +0 -41
- data/test/blocking_commands_test.rb +0 -42
- data/test/command_map_test.rb +0 -30
- data/test/commands_on_hashes_test.rb +0 -21
- data/test/commands_on_lists_test.rb +0 -20
- data/test/commands_on_sets_test.rb +0 -77
- data/test/commands_on_sorted_sets_test.rb +0 -109
- data/test/commands_on_strings_test.rb +0 -83
- data/test/commands_on_value_types_test.rb +0 -99
- data/test/connection_handling_test.rb +0 -189
- data/test/db/.gitignore +0 -1
- data/test/distributed_blocking_commands_test.rb +0 -46
- data/test/distributed_commands_on_hashes_test.rb +0 -10
- data/test/distributed_commands_on_lists_test.rb +0 -22
- data/test/distributed_commands_on_sets_test.rb +0 -83
- data/test/distributed_commands_on_sorted_sets_test.rb +0 -18
- data/test/distributed_commands_on_strings_test.rb +0 -48
- data/test/distributed_commands_on_value_types_test.rb +0 -87
- data/test/distributed_commands_requiring_clustering_test.rb +0 -148
- data/test/distributed_connection_handling_test.rb +0 -23
- data/test/distributed_internals_test.rb +0 -15
- data/test/distributed_key_tags_test.rb +0 -52
- data/test/distributed_persistence_control_commands_test.rb +0 -26
- data/test/distributed_publish_subscribe_test.rb +0 -92
- data/test/distributed_remote_server_control_commands_test.rb +0 -53
- data/test/distributed_scripting_test.rb +0 -102
- data/test/distributed_sorting_test.rb +0 -20
- data/test/distributed_test.rb +0 -58
- data/test/distributed_transactions_test.rb +0 -32
- data/test/encoding_test.rb +0 -18
- data/test/error_replies_test.rb +0 -59
- data/test/helper.rb +0 -188
- data/test/helper_test.rb +0 -22
- data/test/internals_test.rb +0 -214
- data/test/lint/blocking_commands.rb +0 -124
- data/test/lint/hashes.rb +0 -162
- data/test/lint/lists.rb +0 -143
- data/test/lint/sets.rb +0 -96
- data/test/lint/sorted_sets.rb +0 -201
- data/test/lint/strings.rb +0 -157
- data/test/lint/value_types.rb +0 -106
- data/test/persistence_control_commands_test.rb +0 -26
- data/test/pipelining_commands_test.rb +0 -195
- data/test/publish_subscribe_test.rb +0 -153
- data/test/remote_server_control_commands_test.rb +0 -104
- data/test/scripting_test.rb +0 -78
- data/test/sorting_test.rb +0 -45
- data/test/support/connection/hiredis.rb +0 -1
- data/test/support/connection/ruby.rb +0 -1
- data/test/support/connection/synchrony.rb +0 -17
- data/test/support/redis_mock.rb +0 -92
- data/test/support/wire/synchrony.rb +0 -24
- data/test/support/wire/thread.rb +0 -5
- data/test/synchrony_driver.rb +0 -57
- data/test/test.conf +0 -9
- data/test/thread_safety_test.rb +0 -32
- data/test/transactions_test.rb +0 -244
- data/test/unknown_commands_test.rb +0 -14
- data/test/url_param_test.rb +0 -64
data/lib/redis/distributed.rb
CHANGED
@@ -1,37 +1,47 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "hash_ring"
|
2
4
|
|
3
5
|
class Redis
|
4
6
|
class Distributed
|
5
|
-
|
6
7
|
class CannotDistribute < RuntimeError
|
7
8
|
def initialize(command)
|
8
9
|
@command = command
|
9
10
|
end
|
10
11
|
|
11
12
|
def message
|
12
|
-
"#{@command.to_s.upcase} cannot be used in Redis::Distributed because the keys involved need
|
13
|
+
"#{@command.to_s.upcase} cannot be used in Redis::Distributed because the keys involved need " \
|
14
|
+
"to be on the same server or because we cannot guarantee that the operation will be atomic."
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
16
18
|
attr_reader :ring
|
17
19
|
|
18
|
-
def initialize(
|
19
|
-
@tag = options
|
20
|
-
@
|
21
|
-
@
|
20
|
+
def initialize(node_configs, options = {})
|
21
|
+
@tag = options[:tag] || /^\{(.+?)\}/
|
22
|
+
@ring = options[:ring] || HashRing.new
|
23
|
+
@node_configs = node_configs.dup
|
24
|
+
@default_options = options.dup
|
25
|
+
node_configs.each { |node_config| add_node(node_config) }
|
22
26
|
@subscribed_node = nil
|
27
|
+
@watch_key = nil
|
23
28
|
end
|
24
29
|
|
25
30
|
def node_for(key)
|
26
|
-
|
31
|
+
key = key_tag(key.to_s) || key.to_s
|
32
|
+
raise CannotDistribute, :watch if @watch_key && @watch_key != key
|
33
|
+
|
34
|
+
@ring.get_node(key)
|
27
35
|
end
|
28
36
|
|
29
37
|
def nodes
|
30
38
|
@ring.nodes
|
31
39
|
end
|
32
40
|
|
33
|
-
def add_node(
|
34
|
-
|
41
|
+
def add_node(options)
|
42
|
+
options = { url: options } if options.is_a?(String)
|
43
|
+
options = @default_options.merge(options)
|
44
|
+
@ring.add_node Redis.new(options)
|
35
45
|
end
|
36
46
|
|
37
47
|
# Change the selected database for the current connection.
|
@@ -134,6 +144,21 @@ class Redis
|
|
134
144
|
node_for(key).pttl(key)
|
135
145
|
end
|
136
146
|
|
147
|
+
# Return a serialized version of the value stored at a key.
|
148
|
+
def dump(key)
|
149
|
+
node_for(key).dump(key)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Create a key using the serialized value, previously obtained using DUMP.
|
153
|
+
def restore(key, ttl, serialized_value, **options)
|
154
|
+
node_for(key).restore(key, ttl, serialized_value, **options)
|
155
|
+
end
|
156
|
+
|
157
|
+
# Transfer a key from the connected instance to another instance.
|
158
|
+
def migrate(_key, _options)
|
159
|
+
raise CannotDistribute, :migrate
|
160
|
+
end
|
161
|
+
|
137
162
|
# Delete a key.
|
138
163
|
def del(*args)
|
139
164
|
keys_per_node = args.group_by { |key| node_for(key) }
|
@@ -142,9 +167,42 @@ class Redis
|
|
142
167
|
end
|
143
168
|
end
|
144
169
|
|
170
|
+
# Unlink keys.
|
171
|
+
def unlink(*args)
|
172
|
+
keys_per_node = args.group_by { |key| node_for(key) }
|
173
|
+
keys_per_node.inject(0) do |sum, (node, keys)|
|
174
|
+
sum + node.unlink(*keys)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
145
178
|
# Determine if a key exists.
|
146
|
-
def exists(
|
147
|
-
|
179
|
+
def exists(*args)
|
180
|
+
if !Redis.exists_returns_integer && args.size == 1
|
181
|
+
message = "`Redis#exists(key)` will return an Integer in redis-rb 4.3, if you want to keep the old behavior, " \
|
182
|
+
"use `exists?` instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = true. " \
|
183
|
+
"(#{::Kernel.caller(1, 1).first})\n"
|
184
|
+
|
185
|
+
if defined?(::Warning)
|
186
|
+
::Warning.warn(message)
|
187
|
+
else
|
188
|
+
warn(message)
|
189
|
+
end
|
190
|
+
exists?(*args)
|
191
|
+
else
|
192
|
+
keys_per_node = args.group_by { |key| node_for(key) }
|
193
|
+
keys_per_node.inject(0) do |sum, (node, keys)|
|
194
|
+
sum + node._exists(*keys)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Determine if any of the keys exists.
|
200
|
+
def exists?(*args)
|
201
|
+
keys_per_node = args.group_by { |key| node_for(key) }
|
202
|
+
keys_per_node.each do |node, keys|
|
203
|
+
return true if node.exists?(*keys)
|
204
|
+
end
|
205
|
+
false
|
148
206
|
end
|
149
207
|
|
150
208
|
# Find all keys matching the given pattern.
|
@@ -177,11 +235,11 @@ class Redis
|
|
177
235
|
end
|
178
236
|
|
179
237
|
# Sort the elements in a list, set or sorted set.
|
180
|
-
def sort(key, options
|
238
|
+
def sort(key, **options)
|
181
239
|
keys = [key, options[:by], options[:store], *Array(options[:get])].compact
|
182
240
|
|
183
241
|
ensure_same_node(:sort, keys) do |node|
|
184
|
-
node.sort(key, options)
|
242
|
+
node.sort(key, **options)
|
185
243
|
end
|
186
244
|
end
|
187
245
|
|
@@ -216,8 +274,8 @@ class Redis
|
|
216
274
|
end
|
217
275
|
|
218
276
|
# Set the string value of a key.
|
219
|
-
def set(key, value)
|
220
|
-
node_for(key).set(key, value)
|
277
|
+
def set(key, value, **options)
|
278
|
+
node_for(key).set(key, value, **options)
|
221
279
|
end
|
222
280
|
|
223
281
|
# Set the time to live in seconds of a key.
|
@@ -236,20 +294,20 @@ class Redis
|
|
236
294
|
end
|
237
295
|
|
238
296
|
# Set multiple keys to multiple values.
|
239
|
-
def mset(*
|
297
|
+
def mset(*_args)
|
240
298
|
raise CannotDistribute, :mset
|
241
299
|
end
|
242
300
|
|
243
|
-
def mapped_mset(
|
301
|
+
def mapped_mset(_hash)
|
244
302
|
raise CannotDistribute, :mapped_mset
|
245
303
|
end
|
246
304
|
|
247
305
|
# Set multiple keys to multiple values, only if none of the keys exist.
|
248
|
-
def msetnx(*
|
306
|
+
def msetnx(*_args)
|
249
307
|
raise CannotDistribute, :msetnx
|
250
308
|
end
|
251
309
|
|
252
|
-
def mapped_msetnx(
|
310
|
+
def mapped_msetnx(_hash)
|
253
311
|
raise CannotDistribute, :mapped_msetnx
|
254
312
|
end
|
255
313
|
|
@@ -258,13 +316,26 @@ class Redis
|
|
258
316
|
node_for(key).get(key)
|
259
317
|
end
|
260
318
|
|
261
|
-
# Get the
|
319
|
+
# Get the value of a key and delete it.
|
320
|
+
def getdel(key)
|
321
|
+
node_for(key).getdel(key)
|
322
|
+
end
|
323
|
+
|
324
|
+
# Get the value of a key and sets its time to live based on options.
|
325
|
+
def getex(key, **options)
|
326
|
+
node_for(key).getex(key, **options)
|
327
|
+
end
|
328
|
+
|
329
|
+
# Get the values of all the given keys as an Array.
|
262
330
|
def mget(*keys)
|
263
|
-
|
331
|
+
mapped_mget(*keys).values_at(*keys)
|
264
332
|
end
|
265
333
|
|
334
|
+
# Get the values of all the given keys as a Hash.
|
266
335
|
def mapped_mget(*keys)
|
267
|
-
|
336
|
+
keys.group_by { |k| node_for k }.inject({}) do |results, (node, subkeys)|
|
337
|
+
results.merge! node.mapped_mget(*subkeys)
|
338
|
+
end
|
268
339
|
end
|
269
340
|
|
270
341
|
# Overwrite part of a string at key starting at the specified offset.
|
@@ -292,6 +363,23 @@ class Redis
|
|
292
363
|
node_for(key).append(key, value)
|
293
364
|
end
|
294
365
|
|
366
|
+
# Count the number of set bits in a range of the string value stored at key.
|
367
|
+
def bitcount(key, start = 0, stop = -1)
|
368
|
+
node_for(key).bitcount(key, start, stop)
|
369
|
+
end
|
370
|
+
|
371
|
+
# Perform a bitwise operation between strings and store the resulting string in a key.
|
372
|
+
def bitop(operation, destkey, *keys)
|
373
|
+
ensure_same_node(:bitop, [destkey] + keys) do |node|
|
374
|
+
node.bitop(operation, destkey, *keys)
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
# Return the position of the first bit set to 1 or 0 in a string.
|
379
|
+
def bitpos(key, bit, start = nil, stop = nil)
|
380
|
+
node_for(key).bitpos(key, bit, start, stop)
|
381
|
+
end
|
382
|
+
|
295
383
|
# Set the string value of a key and return its old value.
|
296
384
|
def getset(key, value)
|
297
385
|
node_for(key).getset(key, value)
|
@@ -306,7 +394,7 @@ class Redis
|
|
306
394
|
get(key)
|
307
395
|
end
|
308
396
|
|
309
|
-
def []=(key,value)
|
397
|
+
def []=(key, value)
|
310
398
|
set(key, value)
|
311
399
|
end
|
312
400
|
|
@@ -315,6 +403,21 @@ class Redis
|
|
315
403
|
node_for(key).llen(key)
|
316
404
|
end
|
317
405
|
|
406
|
+
# Remove the first/last element in a list, append/prepend it to another list and return it.
|
407
|
+
def lmove(source, destination, where_source, where_destination)
|
408
|
+
ensure_same_node(:lmove, [source, destination]) do |node|
|
409
|
+
node.lmove(source, destination, where_source, where_destination)
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
# Remove the first/last element in a list and append/prepend it
|
414
|
+
# to another list and return it, or block until one is available.
|
415
|
+
def blmove(source, destination, where_source, where_destination, timeout: 0)
|
416
|
+
ensure_same_node(:lmove, [source, destination]) do |node|
|
417
|
+
node.blmove(source, destination, where_source, where_destination, timeout: timeout)
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
318
421
|
# Prepend one or more values to a list.
|
319
422
|
def lpush(key, value)
|
320
423
|
node_for(key).lpush(key, value)
|
@@ -335,14 +438,14 @@ class Redis
|
|
335
438
|
node_for(key).rpushx(key, value)
|
336
439
|
end
|
337
440
|
|
338
|
-
# Remove and get the first
|
339
|
-
def lpop(key)
|
340
|
-
node_for(key).lpop(key)
|
441
|
+
# Remove and get the first elements in a list.
|
442
|
+
def lpop(key, count = nil)
|
443
|
+
node_for(key).lpop(key, count)
|
341
444
|
end
|
342
445
|
|
343
|
-
# Remove and get the last
|
344
|
-
def rpop(key)
|
345
|
-
node_for(key).rpop(key)
|
446
|
+
# Remove and get the last elements in a list.
|
447
|
+
def rpop(key, count = nil)
|
448
|
+
node_for(key).rpop(key, count)
|
346
449
|
end
|
347
450
|
|
348
451
|
# Remove the last element in a list, append it to another list and return
|
@@ -354,14 +457,12 @@ class Redis
|
|
354
457
|
end
|
355
458
|
|
356
459
|
def _bpop(cmd, args)
|
357
|
-
|
358
|
-
|
359
|
-
case args.last
|
360
|
-
when Hash
|
460
|
+
timeout = if args.last.is_a?(Hash)
|
361
461
|
options = args.pop
|
362
|
-
|
462
|
+
options[:timeout]
|
463
|
+
elsif args.last.respond_to?(:to_int)
|
363
464
|
# Issue deprecation notice in obnoxious mode...
|
364
|
-
|
465
|
+
args.pop.to_int
|
365
466
|
end
|
366
467
|
|
367
468
|
if args.size > 1
|
@@ -371,7 +472,11 @@ class Redis
|
|
371
472
|
keys = args.flatten
|
372
473
|
|
373
474
|
ensure_same_node(cmd, keys) do |node|
|
374
|
-
|
475
|
+
if timeout
|
476
|
+
node.__send__(cmd, keys, timeout: timeout)
|
477
|
+
else
|
478
|
+
node.__send__(cmd, keys)
|
479
|
+
end
|
375
480
|
end
|
376
481
|
end
|
377
482
|
|
@@ -389,15 +494,9 @@ class Redis
|
|
389
494
|
|
390
495
|
# Pop a value from a list, push it to another list and return it; or block
|
391
496
|
# until one is available.
|
392
|
-
def brpoplpush(source, destination,
|
393
|
-
case options
|
394
|
-
when Integer
|
395
|
-
# Issue deprecation notice in obnoxious mode...
|
396
|
-
options = { :timeout => options }
|
397
|
-
end
|
398
|
-
|
497
|
+
def brpoplpush(source, destination, deprecated_timeout = 0, **options)
|
399
498
|
ensure_same_node(:brpoplpush, [source, destination]) do |node|
|
400
|
-
node.brpoplpush(source, destination, options)
|
499
|
+
node.brpoplpush(source, destination, deprecated_timeout, **options)
|
401
500
|
end
|
402
501
|
end
|
403
502
|
|
@@ -447,13 +546,13 @@ class Redis
|
|
447
546
|
end
|
448
547
|
|
449
548
|
# Remove and return a random member from a set.
|
450
|
-
def spop(key)
|
451
|
-
node_for(key).spop(key)
|
549
|
+
def spop(key, count = nil)
|
550
|
+
node_for(key).spop(key, count)
|
452
551
|
end
|
453
552
|
|
454
553
|
# Get a random member from a set.
|
455
|
-
def srandmember(key)
|
456
|
-
node_for(key).srandmember(key)
|
554
|
+
def srandmember(key, count = nil)
|
555
|
+
node_for(key).srandmember(key, count)
|
457
556
|
end
|
458
557
|
|
459
558
|
# Move a member from one set to another.
|
@@ -468,11 +567,26 @@ class Redis
|
|
468
567
|
node_for(key).sismember(key, member)
|
469
568
|
end
|
470
569
|
|
570
|
+
# Determine if multiple values are members of a set.
|
571
|
+
def smismember(key, *members)
|
572
|
+
node_for(key).smismember(key, *members)
|
573
|
+
end
|
574
|
+
|
471
575
|
# Get all the members in a set.
|
472
576
|
def smembers(key)
|
473
577
|
node_for(key).smembers(key)
|
474
578
|
end
|
475
579
|
|
580
|
+
# Scan a set
|
581
|
+
def sscan(key, cursor, **options)
|
582
|
+
node_for(key).sscan(key, cursor, **options)
|
583
|
+
end
|
584
|
+
|
585
|
+
# Scan a set and return an enumerator
|
586
|
+
def sscan_each(key, **options, &block)
|
587
|
+
node_for(key).sscan_each(key, **options, &block)
|
588
|
+
end
|
589
|
+
|
476
590
|
# Subtract multiple sets.
|
477
591
|
def sdiff(*keys)
|
478
592
|
ensure_same_node(:sdiff, keys) do |node|
|
@@ -525,6 +639,7 @@ class Redis
|
|
525
639
|
def zadd(key, *args)
|
526
640
|
node_for(key).zadd(key, *args)
|
527
641
|
end
|
642
|
+
ruby2_keywords(:zadd) if respond_to?(:ruby2_keywords, true)
|
528
643
|
|
529
644
|
# Increment the score of a member in a sorted set.
|
530
645
|
def zincrby(key, increment, member)
|
@@ -541,15 +656,25 @@ class Redis
|
|
541
656
|
node_for(key).zscore(key, member)
|
542
657
|
end
|
543
658
|
|
659
|
+
# Get one or more random members from a sorted set.
|
660
|
+
def zrandmember(key, count = nil, **options)
|
661
|
+
node_for(key).zrandmember(key, count, **options)
|
662
|
+
end
|
663
|
+
|
664
|
+
# Get the scores associated with the given members in a sorted set.
|
665
|
+
def zmscore(key, *members)
|
666
|
+
node_for(key).zmscore(key, *members)
|
667
|
+
end
|
668
|
+
|
544
669
|
# Return a range of members in a sorted set, by index.
|
545
|
-
def zrange(key, start, stop, options
|
546
|
-
node_for(key).zrange(key, start, stop, options)
|
670
|
+
def zrange(key, start, stop, **options)
|
671
|
+
node_for(key).zrange(key, start, stop, **options)
|
547
672
|
end
|
548
673
|
|
549
674
|
# Return a range of members in a sorted set, by index, with scores ordered
|
550
675
|
# from high to low.
|
551
|
-
def zrevrange(key, start, stop, options
|
552
|
-
node_for(key).zrevrange(key, start, stop, options)
|
676
|
+
def zrevrange(key, start, stop, **options)
|
677
|
+
node_for(key).zrevrange(key, start, stop, **options)
|
553
678
|
end
|
554
679
|
|
555
680
|
# Determine the index of a member in a sorted set.
|
@@ -569,14 +694,14 @@ class Redis
|
|
569
694
|
end
|
570
695
|
|
571
696
|
# Return a range of members in a sorted set, by score.
|
572
|
-
def zrangebyscore(key, min, max, options
|
573
|
-
node_for(key).zrangebyscore(key, min, max, options)
|
697
|
+
def zrangebyscore(key, min, max, **options)
|
698
|
+
node_for(key).zrangebyscore(key, min, max, **options)
|
574
699
|
end
|
575
700
|
|
576
701
|
# Return a range of members in a sorted set, by score, with scores ordered
|
577
702
|
# from high to low.
|
578
|
-
def zrevrangebyscore(key, max, min, options
|
579
|
-
node_for(key).zrevrangebyscore(key, max, min, options)
|
703
|
+
def zrevrangebyscore(key, max, min, **options)
|
704
|
+
node_for(key).zrevrangebyscore(key, max, min, **options)
|
580
705
|
end
|
581
706
|
|
582
707
|
# Remove all members in a sorted set within the given scores.
|
@@ -589,18 +714,25 @@ class Redis
|
|
589
714
|
node_for(key).zcount(key, min, max)
|
590
715
|
end
|
591
716
|
|
717
|
+
# Get the intersection of multiple sorted sets
|
718
|
+
def zinter(*keys, **options)
|
719
|
+
ensure_same_node(:zinter, keys) do |node|
|
720
|
+
node.zinter(*keys, **options)
|
721
|
+
end
|
722
|
+
end
|
723
|
+
|
592
724
|
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
593
725
|
# key.
|
594
|
-
def zinterstore(destination, keys, options
|
726
|
+
def zinterstore(destination, keys, **options)
|
595
727
|
ensure_same_node(:zinterstore, [destination] + keys) do |node|
|
596
|
-
node.zinterstore(destination, keys, options)
|
728
|
+
node.zinterstore(destination, keys, **options)
|
597
729
|
end
|
598
730
|
end
|
599
731
|
|
600
732
|
# Add multiple sorted sets and store the resulting sorted set in a new key.
|
601
|
-
def zunionstore(destination, keys, options
|
733
|
+
def zunionstore(destination, keys, **options)
|
602
734
|
ensure_same_node(:zunionstore, [destination] + keys) do |node|
|
603
|
-
node.zunionstore(destination, keys, options)
|
735
|
+
node.zunionstore(destination, keys, **options)
|
604
736
|
end
|
605
737
|
end
|
606
738
|
|
@@ -609,9 +741,9 @@ class Redis
|
|
609
741
|
node_for(key).hlen(key)
|
610
742
|
end
|
611
743
|
|
612
|
-
# Set
|
613
|
-
def hset(key,
|
614
|
-
node_for(key).hset(key,
|
744
|
+
# Set multiple hash fields to multiple values.
|
745
|
+
def hset(key, *attrs)
|
746
|
+
node_for(key).hset(key, *attrs)
|
615
747
|
end
|
616
748
|
|
617
749
|
# Set the value of a hash field, only if the field does not exist.
|
@@ -643,8 +775,8 @@ class Redis
|
|
643
775
|
end
|
644
776
|
|
645
777
|
# Delete one or more hash fields.
|
646
|
-
def hdel(key,
|
647
|
-
node_for(key).hdel(key,
|
778
|
+
def hdel(key, *fields)
|
779
|
+
node_for(key).hdel(key, *fields)
|
648
780
|
end
|
649
781
|
|
650
782
|
# Determine if a hash field exists.
|
@@ -683,7 +815,7 @@ class Redis
|
|
683
815
|
end
|
684
816
|
|
685
817
|
def subscribed?
|
686
|
-
|
818
|
+
!!@subscribed_node
|
687
819
|
end
|
688
820
|
|
689
821
|
# Listen for messages published to the given channels.
|
@@ -701,7 +833,8 @@ class Redis
|
|
701
833
|
|
702
834
|
# Stop listening for messages posted to the given channels.
|
703
835
|
def unsubscribe(*channels)
|
704
|
-
raise
|
836
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
837
|
+
|
705
838
|
@subscribed_node.unsubscribe(*channels)
|
706
839
|
end
|
707
840
|
|
@@ -717,13 +850,26 @@ class Redis
|
|
717
850
|
end
|
718
851
|
|
719
852
|
# Watch the given keys to determine execution of the MULTI/EXEC block.
|
720
|
-
def watch(*keys)
|
721
|
-
|
853
|
+
def watch(*keys, &block)
|
854
|
+
ensure_same_node(:watch, keys) do |node|
|
855
|
+
@watch_key = key_tag(keys.first) || keys.first.to_s
|
856
|
+
|
857
|
+
begin
|
858
|
+
node.watch(*keys, &block)
|
859
|
+
rescue StandardError
|
860
|
+
@watch_key = nil
|
861
|
+
raise
|
862
|
+
end
|
863
|
+
end
|
722
864
|
end
|
723
865
|
|
724
866
|
# Forget about all watched keys.
|
725
867
|
def unwatch
|
726
|
-
raise CannotDistribute, :unwatch
|
868
|
+
raise CannotDistribute, :unwatch unless @watch_key
|
869
|
+
|
870
|
+
result = node_for(@watch_key).unwatch
|
871
|
+
@watch_key = nil
|
872
|
+
result
|
727
873
|
end
|
728
874
|
|
729
875
|
def pipelined
|
@@ -731,18 +877,30 @@ class Redis
|
|
731
877
|
end
|
732
878
|
|
733
879
|
# Mark the start of a transaction block.
|
734
|
-
def multi
|
735
|
-
raise CannotDistribute, :multi
|
880
|
+
def multi(&block)
|
881
|
+
raise CannotDistribute, :multi unless @watch_key
|
882
|
+
|
883
|
+
result = node_for(@watch_key).multi(&block)
|
884
|
+
@watch_key = nil if block_given?
|
885
|
+
result
|
736
886
|
end
|
737
887
|
|
738
888
|
# Execute all commands issued after MULTI.
|
739
889
|
def exec
|
740
|
-
raise CannotDistribute, :exec
|
890
|
+
raise CannotDistribute, :exec unless @watch_key
|
891
|
+
|
892
|
+
result = node_for(@watch_key).exec
|
893
|
+
@watch_key = nil
|
894
|
+
result
|
741
895
|
end
|
742
896
|
|
743
897
|
# Discard all commands issued after MULTI.
|
744
898
|
def discard
|
745
|
-
raise CannotDistribute, :discard
|
899
|
+
raise CannotDistribute, :discard unless @watch_key
|
900
|
+
|
901
|
+
result = node_for(@watch_key).discard
|
902
|
+
@watch_key = nil
|
903
|
+
result
|
746
904
|
end
|
747
905
|
|
748
906
|
# Control remote script registry.
|
@@ -750,6 +908,26 @@ class Redis
|
|
750
908
|
on_each_node(:script, subcommand, *args)
|
751
909
|
end
|
752
910
|
|
911
|
+
# Add one or more members to a HyperLogLog structure.
|
912
|
+
def pfadd(key, member)
|
913
|
+
node_for(key).pfadd(key, member)
|
914
|
+
end
|
915
|
+
|
916
|
+
# Get the approximate cardinality of members added to HyperLogLog structure.
|
917
|
+
def pfcount(*keys)
|
918
|
+
ensure_same_node(:pfcount, keys.flatten(1)) do |node|
|
919
|
+
node.pfcount(keys)
|
920
|
+
end
|
921
|
+
end
|
922
|
+
|
923
|
+
# Merge multiple HyperLogLog values into an unique value that will approximate the cardinality of the union of
|
924
|
+
# the observed Sets of the source HyperLogLog structures.
|
925
|
+
def pfmerge(dest_key, *source_key)
|
926
|
+
ensure_same_node(:pfmerge, [dest_key, *source_key]) do |node|
|
927
|
+
node.pfmerge(dest_key, *source_key)
|
928
|
+
end
|
929
|
+
end
|
930
|
+
|
753
931
|
def _eval(cmd, args)
|
754
932
|
script = args.shift
|
755
933
|
options = args.pop if args.last.is_a?(Hash)
|
@@ -777,7 +955,11 @@ class Redis
|
|
777
955
|
"#<Redis client v#{Redis::VERSION} for #{nodes.map(&:id).join(', ')}>"
|
778
956
|
end
|
779
957
|
|
780
|
-
|
958
|
+
def dup
|
959
|
+
self.class.new(@node_configs, @default_options)
|
960
|
+
end
|
961
|
+
|
962
|
+
protected
|
781
963
|
|
782
964
|
def on_each_node(command, *args)
|
783
965
|
nodes.map do |node|
|
data/lib/redis/errors.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class Redis
|
2
4
|
# Base error for all redis-rb errors.
|
3
5
|
class BaseError < RuntimeError
|
@@ -37,4 +39,50 @@ class Redis
|
|
37
39
|
# Raised when the connection was inherited by a child process.
|
38
40
|
class InheritedError < BaseConnectionError
|
39
41
|
end
|
42
|
+
|
43
|
+
# Raised when client options are invalid.
|
44
|
+
class InvalidClientOptionError < BaseError
|
45
|
+
end
|
46
|
+
|
47
|
+
class Cluster
|
48
|
+
# Raised when client connected to redis as cluster mode
|
49
|
+
# and some cluster subcommands were called.
|
50
|
+
class OrchestrationCommandNotSupported < BaseError
|
51
|
+
def initialize(command, subcommand = '')
|
52
|
+
str = [command, subcommand].map(&:to_s).reject(&:empty?).join(' ').upcase
|
53
|
+
msg = "#{str} command should be used with care "\
|
54
|
+
'only by applications orchestrating Redis Cluster, like redis-trib, '\
|
55
|
+
'and the command if used out of the right context can leave the cluster '\
|
56
|
+
'in a wrong state or cause data loss.'
|
57
|
+
super(msg)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Raised when error occurs on any node of cluster.
|
62
|
+
class CommandErrorCollection < BaseError
|
63
|
+
attr_reader :errors
|
64
|
+
|
65
|
+
# @param errors [Hash{String => Redis::CommandError}]
|
66
|
+
# @param error_message [String]
|
67
|
+
def initialize(errors, error_message = 'Command errors were replied on any node')
|
68
|
+
@errors = errors
|
69
|
+
super(error_message)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Raised when cluster client can't select node.
|
74
|
+
class AmbiguousNodeError < BaseError
|
75
|
+
def initialize(command)
|
76
|
+
super("Cluster client doesn't know which node the #{command} command should be sent to.")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Raised when commands in pipelining include cross slot keys.
|
81
|
+
class CrossSlotPipeliningError < BaseError
|
82
|
+
def initialize(keys)
|
83
|
+
super("Cluster client couldn't send pipelining to single node. "\
|
84
|
+
"The commands include cross slot keys. #{keys}")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
40
88
|
end
|