redis 3.3.5 → 4.7.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 +5 -5
- data/CHANGELOG.md +220 -2
- data/README.md +169 -89
- data/lib/redis/client.rb +176 -99
- data/lib/redis/cluster/command.rb +79 -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 +120 -0
- data/lib/redis/cluster/node_key.rb +31 -0
- data/lib/redis/cluster/node_loader.rb +34 -0
- data/lib/redis/cluster/option.rb +100 -0
- data/lib/redis/cluster/slot.rb +86 -0
- data/lib/redis/cluster/slot_loader.rb +46 -0
- data/lib/redis/cluster.rb +315 -0
- data/lib/redis/commands/bitmaps.rb +63 -0
- data/lib/redis/commands/cluster.rb +45 -0
- data/lib/redis/commands/connection.rb +58 -0
- data/lib/redis/commands/geo.rb +84 -0
- data/lib/redis/commands/hashes.rb +251 -0
- data/lib/redis/commands/hyper_log_log.rb +37 -0
- data/lib/redis/commands/keys.rb +411 -0
- data/lib/redis/commands/lists.rb +289 -0
- data/lib/redis/commands/pubsub.rb +72 -0
- data/lib/redis/commands/scripting.rb +114 -0
- data/lib/redis/commands/server.rb +188 -0
- data/lib/redis/commands/sets.rb +207 -0
- data/lib/redis/commands/sorted_sets.rb +812 -0
- data/lib/redis/commands/streams.rb +382 -0
- data/lib/redis/commands/strings.rb +313 -0
- data/lib/redis/commands/transactions.rb +139 -0
- data/lib/redis/commands.rb +242 -0
- data/lib/redis/connection/command_helper.rb +7 -10
- data/lib/redis/connection/hiredis.rb +5 -5
- data/lib/redis/connection/registry.rb +2 -1
- data/lib/redis/connection/ruby.rb +130 -128
- data/lib/redis/connection/synchrony.rb +24 -9
- data/lib/redis/connection.rb +3 -1
- data/lib/redis/distributed.rb +231 -72
- data/lib/redis/errors.rb +57 -0
- data/lib/redis/hash_ring.rb +30 -73
- data/lib/redis/pipeline.rb +178 -13
- data/lib/redis/subscribe.rb +11 -12
- data/lib/redis/version.rb +3 -1
- data/lib/redis.rb +173 -2661
- metadata +66 -202
- data/.gitignore +0 -16
- data/.travis/Gemfile +0 -11
- data/.travis.yml +0 -89
- data/.yardopts +0 -3
- data/Gemfile +0 -4
- data/Rakefile +0 -87
- data/benchmarking/logging.rb +0 -71
- 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/consistency.rb +0 -114
- 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 -37
- data/examples/sentinel/sentinel.conf +0 -9
- data/examples/sentinel/start +0 -49
- data/examples/sentinel.rb +0 -41
- data/examples/sets.rb +0 -36
- data/examples/unicorn/config.ru +0 -3
- data/examples/unicorn/unicorn.rb +0 -20
- data/redis.gemspec +0 -44
- data/test/bitpos_test.rb +0 -69
- data/test/blocking_commands_test.rb +0 -42
- data/test/client_test.rb +0 -59
- data/test/command_map_test.rb +0 -30
- data/test/commands_on_hashes_test.rb +0 -21
- data/test/commands_on_hyper_log_log_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 -137
- data/test/commands_on_strings_test.rb +0 -101
- data/test/commands_on_value_types_test.rb +0 -133
- data/test/connection_handling_test.rb +0 -277
- data/test/connection_test.rb +0 -57
- data/test/db/.gitkeep +0 -0
- data/test/distributed_blocking_commands_test.rb +0 -46
- data/test/distributed_commands_on_hashes_test.rb +0 -10
- data/test/distributed_commands_on_hyper_log_log_test.rb +0 -33
- 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 -59
- data/test/distributed_commands_on_value_types_test.rb +0 -95
- data/test/distributed_commands_requiring_clustering_test.rb +0 -164
- data/test/distributed_connection_handling_test.rb +0 -23
- data/test/distributed_internals_test.rb +0 -79
- 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 -66
- 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/fork_safety_test.rb +0 -65
- data/test/helper.rb +0 -232
- data/test/helper_test.rb +0 -24
- data/test/internals_test.rb +0 -417
- data/test/lint/blocking_commands.rb +0 -150
- data/test/lint/hashes.rb +0 -162
- data/test/lint/hyper_log_log.rb +0 -60
- data/test/lint/lists.rb +0 -143
- data/test/lint/sets.rb +0 -140
- data/test/lint/sorted_sets.rb +0 -316
- data/test/lint/strings.rb +0 -260
- data/test/lint/value_types.rb +0 -122
- data/test/persistence_control_commands_test.rb +0 -26
- data/test/pipelining_commands_test.rb +0 -242
- data/test/publish_subscribe_test.rb +0 -282
- data/test/remote_server_control_commands_test.rb +0 -118
- data/test/scanning_test.rb +0 -413
- data/test/scripting_test.rb +0 -78
- data/test/sentinel_command_test.rb +0 -80
- data/test/sentinel_test.rb +0 -255
- data/test/sorting_test.rb +0 -59
- data/test/ssl_test.rb +0 -73
- 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 -130
- data/test/support/ssl/gen_certs.sh +0 -31
- data/test/support/ssl/trusted-ca.crt +0 -25
- data/test/support/ssl/trusted-ca.key +0 -27
- data/test/support/ssl/trusted-cert.crt +0 -81
- data/test/support/ssl/trusted-cert.key +0 -28
- data/test/support/ssl/untrusted-ca.crt +0 -26
- data/test/support/ssl/untrusted-ca.key +0 -27
- data/test/support/ssl/untrusted-cert.crt +0 -82
- data/test/support/ssl/untrusted-cert.key +0 -28
- data/test/support/wire/synchrony.rb +0 -24
- data/test/support/wire/thread.rb +0 -5
- data/test/synchrony_driver.rb +0 -88
- data/test/test.conf.erb +0 -9
- data/test/thread_safety_test.rb +0 -62
- data/test/transactions_test.rb +0 -264
- data/test/unknown_commands_test.rb +0 -14
- data/test/url_param_test.rb +0 -138
data/lib/redis/distributed.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "redis/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
|
|
@@ -22,10 +24,14 @@ class Redis
|
|
22
24
|
@default_options = options.dup
|
23
25
|
node_configs.each { |node_config| add_node(node_config) }
|
24
26
|
@subscribed_node = nil
|
27
|
+
@watch_key = nil
|
25
28
|
end
|
26
29
|
|
27
30
|
def node_for(key)
|
28
|
-
|
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)
|
29
35
|
end
|
30
36
|
|
31
37
|
def nodes
|
@@ -33,9 +39,9 @@ class Redis
|
|
33
39
|
end
|
34
40
|
|
35
41
|
def add_node(options)
|
36
|
-
options = { :
|
42
|
+
options = { url: options } if options.is_a?(String)
|
37
43
|
options = @default_options.merge(options)
|
38
|
-
@ring.add_node Redis.new(
|
44
|
+
@ring.add_node Redis.new(options)
|
39
45
|
end
|
40
46
|
|
41
47
|
# Change the selected database for the current connection.
|
@@ -144,12 +150,12 @@ class Redis
|
|
144
150
|
end
|
145
151
|
|
146
152
|
# Create a key using the serialized value, previously obtained using DUMP.
|
147
|
-
def restore(key, ttl, serialized_value)
|
148
|
-
node_for(key).restore(key, ttl, serialized_value)
|
153
|
+
def restore(key, ttl, serialized_value, **options)
|
154
|
+
node_for(key).restore(key, ttl, serialized_value, **options)
|
149
155
|
end
|
150
156
|
|
151
157
|
# Transfer a key from the connected instance to another instance.
|
152
|
-
def migrate(
|
158
|
+
def migrate(_key, _options)
|
153
159
|
raise CannotDistribute, :migrate
|
154
160
|
end
|
155
161
|
|
@@ -161,9 +167,38 @@ class Redis
|
|
161
167
|
end
|
162
168
|
end
|
163
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
|
+
|
164
178
|
# Determine if a key exists.
|
165
|
-
def exists(
|
166
|
-
|
179
|
+
def exists(*args)
|
180
|
+
if !Redis.exists_returns_integer && args.size == 1
|
181
|
+
::Redis.deprecate!(
|
182
|
+
"`Redis#exists(key)` will return an Integer in redis-rb 4.3, if you want to keep the old behavior, " \
|
183
|
+
"use `exists?` instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = true. " \
|
184
|
+
"(#{::Kernel.caller(1, 1).first})\n"
|
185
|
+
)
|
186
|
+
exists?(*args)
|
187
|
+
else
|
188
|
+
keys_per_node = args.group_by { |key| node_for(key) }
|
189
|
+
keys_per_node.inject(0) do |sum, (node, keys)|
|
190
|
+
sum + node._exists(*keys)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Determine if any of the keys exists.
|
196
|
+
def exists?(*args)
|
197
|
+
keys_per_node = args.group_by { |key| node_for(key) }
|
198
|
+
keys_per_node.each do |node, keys|
|
199
|
+
return true if node.exists?(*keys)
|
200
|
+
end
|
201
|
+
false
|
167
202
|
end
|
168
203
|
|
169
204
|
# Find all keys matching the given pattern.
|
@@ -176,6 +211,13 @@ class Redis
|
|
176
211
|
node_for(key).move(key, db)
|
177
212
|
end
|
178
213
|
|
214
|
+
# Copy a value from one key to another.
|
215
|
+
def copy(source, destination, **options)
|
216
|
+
ensure_same_node(:copy, [source, destination]) do |node|
|
217
|
+
node.copy(source, destination, **options)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
179
221
|
# Return a random key from the keyspace.
|
180
222
|
def randomkey
|
181
223
|
raise CannotDistribute, :randomkey
|
@@ -196,11 +238,11 @@ class Redis
|
|
196
238
|
end
|
197
239
|
|
198
240
|
# Sort the elements in a list, set or sorted set.
|
199
|
-
def sort(key, options
|
241
|
+
def sort(key, **options)
|
200
242
|
keys = [key, options[:by], options[:store], *Array(options[:get])].compact
|
201
243
|
|
202
244
|
ensure_same_node(:sort, keys) do |node|
|
203
|
-
node.sort(key, options)
|
245
|
+
node.sort(key, **options)
|
204
246
|
end
|
205
247
|
end
|
206
248
|
|
@@ -235,8 +277,8 @@ class Redis
|
|
235
277
|
end
|
236
278
|
|
237
279
|
# Set the string value of a key.
|
238
|
-
def set(key, value, options
|
239
|
-
node_for(key).set(key, value, options)
|
280
|
+
def set(key, value, **options)
|
281
|
+
node_for(key).set(key, value, **options)
|
240
282
|
end
|
241
283
|
|
242
284
|
# Set the time to live in seconds of a key.
|
@@ -255,20 +297,20 @@ class Redis
|
|
255
297
|
end
|
256
298
|
|
257
299
|
# Set multiple keys to multiple values.
|
258
|
-
def mset(*
|
300
|
+
def mset(*_args)
|
259
301
|
raise CannotDistribute, :mset
|
260
302
|
end
|
261
303
|
|
262
|
-
def mapped_mset(
|
304
|
+
def mapped_mset(_hash)
|
263
305
|
raise CannotDistribute, :mapped_mset
|
264
306
|
end
|
265
307
|
|
266
308
|
# Set multiple keys to multiple values, only if none of the keys exist.
|
267
|
-
def msetnx(*
|
309
|
+
def msetnx(*_args)
|
268
310
|
raise CannotDistribute, :msetnx
|
269
311
|
end
|
270
312
|
|
271
|
-
def mapped_msetnx(
|
313
|
+
def mapped_msetnx(_hash)
|
272
314
|
raise CannotDistribute, :mapped_msetnx
|
273
315
|
end
|
274
316
|
|
@@ -277,13 +319,26 @@ class Redis
|
|
277
319
|
node_for(key).get(key)
|
278
320
|
end
|
279
321
|
|
280
|
-
# Get the
|
322
|
+
# Get the value of a key and delete it.
|
323
|
+
def getdel(key)
|
324
|
+
node_for(key).getdel(key)
|
325
|
+
end
|
326
|
+
|
327
|
+
# Get the value of a key and sets its time to live based on options.
|
328
|
+
def getex(key, **options)
|
329
|
+
node_for(key).getex(key, **options)
|
330
|
+
end
|
331
|
+
|
332
|
+
# Get the values of all the given keys as an Array.
|
281
333
|
def mget(*keys)
|
282
|
-
|
334
|
+
mapped_mget(*keys).values_at(*keys)
|
283
335
|
end
|
284
336
|
|
337
|
+
# Get the values of all the given keys as a Hash.
|
285
338
|
def mapped_mget(*keys)
|
286
|
-
|
339
|
+
keys.group_by { |k| node_for k }.inject({}) do |results, (node, subkeys)|
|
340
|
+
results.merge! node.mapped_mget(*subkeys)
|
341
|
+
end
|
287
342
|
end
|
288
343
|
|
289
344
|
# Overwrite part of a string at key starting at the specified offset.
|
@@ -324,7 +379,7 @@ class Redis
|
|
324
379
|
end
|
325
380
|
|
326
381
|
# Return the position of the first bit set to 1 or 0 in a string.
|
327
|
-
def bitpos(key, bit, start=nil, stop=nil)
|
382
|
+
def bitpos(key, bit, start = nil, stop = nil)
|
328
383
|
node_for(key).bitpos(key, bit, start, stop)
|
329
384
|
end
|
330
385
|
|
@@ -342,7 +397,7 @@ class Redis
|
|
342
397
|
get(key)
|
343
398
|
end
|
344
399
|
|
345
|
-
def []=(key,value)
|
400
|
+
def []=(key, value)
|
346
401
|
set(key, value)
|
347
402
|
end
|
348
403
|
|
@@ -351,6 +406,21 @@ class Redis
|
|
351
406
|
node_for(key).llen(key)
|
352
407
|
end
|
353
408
|
|
409
|
+
# Remove the first/last element in a list, append/prepend it to another list and return it.
|
410
|
+
def lmove(source, destination, where_source, where_destination)
|
411
|
+
ensure_same_node(:lmove, [source, destination]) do |node|
|
412
|
+
node.lmove(source, destination, where_source, where_destination)
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
# Remove the first/last element in a list and append/prepend it
|
417
|
+
# to another list and return it, or block until one is available.
|
418
|
+
def blmove(source, destination, where_source, where_destination, timeout: 0)
|
419
|
+
ensure_same_node(:lmove, [source, destination]) do |node|
|
420
|
+
node.blmove(source, destination, where_source, where_destination, timeout: timeout)
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
354
424
|
# Prepend one or more values to a list.
|
355
425
|
def lpush(key, value)
|
356
426
|
node_for(key).lpush(key, value)
|
@@ -371,14 +441,14 @@ class Redis
|
|
371
441
|
node_for(key).rpushx(key, value)
|
372
442
|
end
|
373
443
|
|
374
|
-
# Remove and get the first
|
375
|
-
def lpop(key)
|
376
|
-
node_for(key).lpop(key)
|
444
|
+
# Remove and get the first elements in a list.
|
445
|
+
def lpop(key, count = nil)
|
446
|
+
node_for(key).lpop(key, count)
|
377
447
|
end
|
378
448
|
|
379
|
-
# Remove and get the last
|
380
|
-
def rpop(key)
|
381
|
-
node_for(key).rpop(key)
|
449
|
+
# Remove and get the last elements in a list.
|
450
|
+
def rpop(key, count = nil)
|
451
|
+
node_for(key).rpop(key, count)
|
382
452
|
end
|
383
453
|
|
384
454
|
# Remove the last element in a list, append it to another list and return
|
@@ -390,14 +460,12 @@ class Redis
|
|
390
460
|
end
|
391
461
|
|
392
462
|
def _bpop(cmd, args)
|
393
|
-
|
394
|
-
|
395
|
-
case args.last
|
396
|
-
when Hash
|
463
|
+
timeout = if args.last.is_a?(Hash)
|
397
464
|
options = args.pop
|
398
|
-
|
465
|
+
options[:timeout]
|
466
|
+
elsif args.last.respond_to?(:to_int)
|
399
467
|
# Issue deprecation notice in obnoxious mode...
|
400
|
-
|
468
|
+
args.pop.to_int
|
401
469
|
end
|
402
470
|
|
403
471
|
if args.size > 1
|
@@ -407,7 +475,11 @@ class Redis
|
|
407
475
|
keys = args.flatten
|
408
476
|
|
409
477
|
ensure_same_node(cmd, keys) do |node|
|
410
|
-
|
478
|
+
if timeout
|
479
|
+
node.__send__(cmd, keys, timeout: timeout)
|
480
|
+
else
|
481
|
+
node.__send__(cmd, keys)
|
482
|
+
end
|
411
483
|
end
|
412
484
|
end
|
413
485
|
|
@@ -425,15 +497,9 @@ class Redis
|
|
425
497
|
|
426
498
|
# Pop a value from a list, push it to another list and return it; or block
|
427
499
|
# until one is available.
|
428
|
-
def brpoplpush(source, destination,
|
429
|
-
case options
|
430
|
-
when Integer
|
431
|
-
# Issue deprecation notice in obnoxious mode...
|
432
|
-
options = { :timeout => options }
|
433
|
-
end
|
434
|
-
|
500
|
+
def brpoplpush(source, destination, deprecated_timeout = 0, **options)
|
435
501
|
ensure_same_node(:brpoplpush, [source, destination]) do |node|
|
436
|
-
node.brpoplpush(source, destination, options)
|
502
|
+
node.brpoplpush(source, destination, deprecated_timeout, **options)
|
437
503
|
end
|
438
504
|
end
|
439
505
|
|
@@ -504,11 +570,26 @@ class Redis
|
|
504
570
|
node_for(key).sismember(key, member)
|
505
571
|
end
|
506
572
|
|
573
|
+
# Determine if multiple values are members of a set.
|
574
|
+
def smismember(key, *members)
|
575
|
+
node_for(key).smismember(key, *members)
|
576
|
+
end
|
577
|
+
|
507
578
|
# Get all the members in a set.
|
508
579
|
def smembers(key)
|
509
580
|
node_for(key).smembers(key)
|
510
581
|
end
|
511
582
|
|
583
|
+
# Scan a set
|
584
|
+
def sscan(key, cursor, **options)
|
585
|
+
node_for(key).sscan(key, cursor, **options)
|
586
|
+
end
|
587
|
+
|
588
|
+
# Scan a set and return an enumerator
|
589
|
+
def sscan_each(key, **options, &block)
|
590
|
+
node_for(key).sscan_each(key, **options, &block)
|
591
|
+
end
|
592
|
+
|
512
593
|
# Subtract multiple sets.
|
513
594
|
def sdiff(*keys)
|
514
595
|
ensure_same_node(:sdiff, keys) do |node|
|
@@ -561,6 +642,7 @@ class Redis
|
|
561
642
|
def zadd(key, *args)
|
562
643
|
node_for(key).zadd(key, *args)
|
563
644
|
end
|
645
|
+
ruby2_keywords(:zadd) if respond_to?(:ruby2_keywords, true)
|
564
646
|
|
565
647
|
# Increment the score of a member in a sorted set.
|
566
648
|
def zincrby(key, increment, member)
|
@@ -577,15 +659,33 @@ class Redis
|
|
577
659
|
node_for(key).zscore(key, member)
|
578
660
|
end
|
579
661
|
|
580
|
-
#
|
581
|
-
def
|
582
|
-
node_for(key).
|
662
|
+
# Get one or more random members from a sorted set.
|
663
|
+
def zrandmember(key, count = nil, **options)
|
664
|
+
node_for(key).zrandmember(key, count, **options)
|
665
|
+
end
|
666
|
+
|
667
|
+
# Get the scores associated with the given members in a sorted set.
|
668
|
+
def zmscore(key, *members)
|
669
|
+
node_for(key).zmscore(key, *members)
|
670
|
+
end
|
671
|
+
|
672
|
+
# Return a range of members in a sorted set, by index, score or lexicographical ordering.
|
673
|
+
def zrange(key, start, stop, **options)
|
674
|
+
node_for(key).zrange(key, start, stop, **options)
|
675
|
+
end
|
676
|
+
|
677
|
+
# Select a range of members in a sorted set, by index, score or lexicographical ordering
|
678
|
+
# and store the resulting sorted set in a new key.
|
679
|
+
def zrangestore(dest_key, src_key, start, stop, **options)
|
680
|
+
ensure_same_node(:zrangestore, [dest_key, src_key]) do |node|
|
681
|
+
node.zrangestore(dest_key, src_key, start, stop, **options)
|
682
|
+
end
|
583
683
|
end
|
584
684
|
|
585
685
|
# Return a range of members in a sorted set, by index, with scores ordered
|
586
686
|
# from high to low.
|
587
|
-
def zrevrange(key, start, stop, options
|
588
|
-
node_for(key).zrevrange(key, start, stop, options)
|
687
|
+
def zrevrange(key, start, stop, **options)
|
688
|
+
node_for(key).zrevrange(key, start, stop, **options)
|
589
689
|
end
|
590
690
|
|
591
691
|
# Determine the index of a member in a sorted set.
|
@@ -605,14 +705,14 @@ class Redis
|
|
605
705
|
end
|
606
706
|
|
607
707
|
# Return a range of members in a sorted set, by score.
|
608
|
-
def zrangebyscore(key, min, max, options
|
609
|
-
node_for(key).zrangebyscore(key, min, max, options)
|
708
|
+
def zrangebyscore(key, min, max, **options)
|
709
|
+
node_for(key).zrangebyscore(key, min, max, **options)
|
610
710
|
end
|
611
711
|
|
612
712
|
# Return a range of members in a sorted set, by score, with scores ordered
|
613
713
|
# from high to low.
|
614
|
-
def zrevrangebyscore(key, max, min, options
|
615
|
-
node_for(key).zrevrangebyscore(key, max, min, options)
|
714
|
+
def zrevrangebyscore(key, max, min, **options)
|
715
|
+
node_for(key).zrevrangebyscore(key, max, min, **options)
|
616
716
|
end
|
617
717
|
|
618
718
|
# Remove all members in a sorted set within the given scores.
|
@@ -625,18 +725,47 @@ class Redis
|
|
625
725
|
node_for(key).zcount(key, min, max)
|
626
726
|
end
|
627
727
|
|
728
|
+
# Get the intersection of multiple sorted sets
|
729
|
+
def zinter(*keys, **options)
|
730
|
+
ensure_same_node(:zinter, keys) do |node|
|
731
|
+
node.zinter(*keys, **options)
|
732
|
+
end
|
733
|
+
end
|
734
|
+
|
628
735
|
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
629
736
|
# key.
|
630
|
-
def zinterstore(destination, keys, options
|
737
|
+
def zinterstore(destination, keys, **options)
|
631
738
|
ensure_same_node(:zinterstore, [destination] + keys) do |node|
|
632
|
-
node.zinterstore(destination, keys, options)
|
739
|
+
node.zinterstore(destination, keys, **options)
|
740
|
+
end
|
741
|
+
end
|
742
|
+
|
743
|
+
# Return the union of multiple sorted sets.
|
744
|
+
def zunion(*keys, **options)
|
745
|
+
ensure_same_node(:zunion, keys) do |node|
|
746
|
+
node.zunion(*keys, **options)
|
633
747
|
end
|
634
748
|
end
|
635
749
|
|
636
750
|
# Add multiple sorted sets and store the resulting sorted set in a new key.
|
637
|
-
def zunionstore(destination, keys, options
|
751
|
+
def zunionstore(destination, keys, **options)
|
638
752
|
ensure_same_node(:zunionstore, [destination] + keys) do |node|
|
639
|
-
node.zunionstore(destination, keys, options)
|
753
|
+
node.zunionstore(destination, keys, **options)
|
754
|
+
end
|
755
|
+
end
|
756
|
+
|
757
|
+
# Return the difference between the first and all successive input sorted sets.
|
758
|
+
def zdiff(*keys, **options)
|
759
|
+
ensure_same_node(:zdiff, keys) do |node|
|
760
|
+
node.zdiff(*keys, **options)
|
761
|
+
end
|
762
|
+
end
|
763
|
+
|
764
|
+
# Compute the difference between the first and all successive input sorted sets
|
765
|
+
# and store the resulting sorted set in a new key.
|
766
|
+
def zdiffstore(destination, keys, **options)
|
767
|
+
ensure_same_node(:zdiffstore, [destination] + keys) do |node|
|
768
|
+
node.zdiffstore(destination, keys, **options)
|
640
769
|
end
|
641
770
|
end
|
642
771
|
|
@@ -645,9 +774,9 @@ class Redis
|
|
645
774
|
node_for(key).hlen(key)
|
646
775
|
end
|
647
776
|
|
648
|
-
# Set
|
649
|
-
def hset(key,
|
650
|
-
node_for(key).hset(key,
|
777
|
+
# Set multiple hash fields to multiple values.
|
778
|
+
def hset(key, *attrs)
|
779
|
+
node_for(key).hset(key, *attrs)
|
651
780
|
end
|
652
781
|
|
653
782
|
# Set the value of a hash field, only if the field does not exist.
|
@@ -678,9 +807,13 @@ class Redis
|
|
678
807
|
Hash[*fields.zip(hmget(key, *fields)).flatten]
|
679
808
|
end
|
680
809
|
|
810
|
+
def hrandfield(key, count = nil, **options)
|
811
|
+
node_for(key).hrandfield(key, count, **options)
|
812
|
+
end
|
813
|
+
|
681
814
|
# Delete one or more hash fields.
|
682
|
-
def hdel(key,
|
683
|
-
node_for(key).hdel(key,
|
815
|
+
def hdel(key, *fields)
|
816
|
+
node_for(key).hdel(key, *fields)
|
684
817
|
end
|
685
818
|
|
686
819
|
# Determine if a hash field exists.
|
@@ -719,7 +852,7 @@ class Redis
|
|
719
852
|
end
|
720
853
|
|
721
854
|
def subscribed?
|
722
|
-
|
855
|
+
!!@subscribed_node
|
723
856
|
end
|
724
857
|
|
725
858
|
# Listen for messages published to the given channels.
|
@@ -737,7 +870,8 @@ class Redis
|
|
737
870
|
|
738
871
|
# Stop listening for messages posted to the given channels.
|
739
872
|
def unsubscribe(*channels)
|
740
|
-
raise
|
873
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
874
|
+
|
741
875
|
@subscribed_node.unsubscribe(*channels)
|
742
876
|
end
|
743
877
|
|
@@ -753,13 +887,26 @@ class Redis
|
|
753
887
|
end
|
754
888
|
|
755
889
|
# Watch the given keys to determine execution of the MULTI/EXEC block.
|
756
|
-
def watch(*keys)
|
757
|
-
|
890
|
+
def watch(*keys, &block)
|
891
|
+
ensure_same_node(:watch, keys) do |node|
|
892
|
+
@watch_key = key_tag(keys.first) || keys.first.to_s
|
893
|
+
|
894
|
+
begin
|
895
|
+
node.watch(*keys, &block)
|
896
|
+
rescue StandardError
|
897
|
+
@watch_key = nil
|
898
|
+
raise
|
899
|
+
end
|
900
|
+
end
|
758
901
|
end
|
759
902
|
|
760
903
|
# Forget about all watched keys.
|
761
904
|
def unwatch
|
762
|
-
raise CannotDistribute, :unwatch
|
905
|
+
raise CannotDistribute, :unwatch unless @watch_key
|
906
|
+
|
907
|
+
result = node_for(@watch_key).unwatch
|
908
|
+
@watch_key = nil
|
909
|
+
result
|
763
910
|
end
|
764
911
|
|
765
912
|
def pipelined
|
@@ -767,18 +914,30 @@ class Redis
|
|
767
914
|
end
|
768
915
|
|
769
916
|
# Mark the start of a transaction block.
|
770
|
-
def multi
|
771
|
-
raise CannotDistribute, :multi
|
917
|
+
def multi(&block)
|
918
|
+
raise CannotDistribute, :multi unless @watch_key
|
919
|
+
|
920
|
+
result = node_for(@watch_key).multi(&block)
|
921
|
+
@watch_key = nil if block_given?
|
922
|
+
result
|
772
923
|
end
|
773
924
|
|
774
925
|
# Execute all commands issued after MULTI.
|
775
926
|
def exec
|
776
|
-
raise CannotDistribute, :exec
|
927
|
+
raise CannotDistribute, :exec unless @watch_key
|
928
|
+
|
929
|
+
result = node_for(@watch_key).exec
|
930
|
+
@watch_key = nil
|
931
|
+
result
|
777
932
|
end
|
778
933
|
|
779
934
|
# Discard all commands issued after MULTI.
|
780
935
|
def discard
|
781
|
-
raise CannotDistribute, :discard
|
936
|
+
raise CannotDistribute, :discard unless @watch_key
|
937
|
+
|
938
|
+
result = node_for(@watch_key).discard
|
939
|
+
@watch_key = nil
|
940
|
+
result
|
782
941
|
end
|
783
942
|
|
784
943
|
# Control remote script registry.
|
@@ -837,7 +996,7 @@ class Redis
|
|
837
996
|
self.class.new(@node_configs, @default_options)
|
838
997
|
end
|
839
998
|
|
840
|
-
|
999
|
+
protected
|
841
1000
|
|
842
1001
|
def on_each_node(command, *args)
|
843
1002
|
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,59 @@ 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 failed to fetch cluster state information by commands.
|
50
|
+
class InitialSetupError < BaseError
|
51
|
+
# @param errors [Array<Redis::BaseError>]
|
52
|
+
def initialize(errors)
|
53
|
+
super("Redis client could not fetch cluster information: #{errors.map(&:message).uniq.join(',')}")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Raised when client connected to redis as cluster mode
|
58
|
+
# and some cluster subcommands were called.
|
59
|
+
class OrchestrationCommandNotSupported < BaseError
|
60
|
+
def initialize(command, subcommand = '')
|
61
|
+
str = [command, subcommand].map(&:to_s).reject(&:empty?).join(' ').upcase
|
62
|
+
msg = "#{str} command should be used with care "\
|
63
|
+
'only by applications orchestrating Redis Cluster, like redis-trib, '\
|
64
|
+
'and the command if used out of the right context can leave the cluster '\
|
65
|
+
'in a wrong state or cause data loss.'
|
66
|
+
super(msg)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Raised when error occurs on any node of cluster.
|
71
|
+
class CommandErrorCollection < BaseError
|
72
|
+
attr_reader :errors
|
73
|
+
|
74
|
+
# @param errors [Hash{String => Redis::CommandError}]
|
75
|
+
# @param error_message [String]
|
76
|
+
def initialize(errors, error_message = 'Command errors were replied on any node')
|
77
|
+
@errors = errors
|
78
|
+
super(error_message)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Raised when cluster client can't select node.
|
83
|
+
class AmbiguousNodeError < BaseError
|
84
|
+
def initialize(command)
|
85
|
+
super("Cluster client doesn't know which node the #{command} command should be sent to.")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Raised when commands in pipelining include cross slot keys.
|
90
|
+
class CrossSlotPipeliningError < BaseError
|
91
|
+
def initialize(keys)
|
92
|
+
super("Cluster client couldn't send pipelining to single node. "\
|
93
|
+
"The commands include cross slot keys. #{keys}")
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
40
97
|
end
|