redis 4.0.1 → 4.8.1
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 -0
- data/README.md +152 -28
- data/lib/redis/client.rb +171 -107
- 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 +455 -0
- data/lib/redis/commands/lists.rb +290 -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 +223 -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 +240 -0
- data/lib/redis/connection/command_helper.rb +5 -2
- data/lib/redis/connection/hiredis.rb +7 -5
- data/lib/redis/connection/registry.rb +2 -1
- data/lib/redis/connection/ruby.rb +139 -111
- data/lib/redis/connection/synchrony.rb +17 -10
- data/lib/redis/connection.rb +3 -1
- data/lib/redis/distributed.rb +244 -87
- data/lib/redis/errors.rb +57 -0
- data/lib/redis/hash_ring.rb +15 -14
- data/lib/redis/pipeline.rb +181 -10
- data/lib/redis/subscribe.rb +11 -12
- data/lib/redis/version.rb +3 -1
- data/lib/redis.rb +180 -2716
- metadata +45 -195
- data/.gitignore +0 -16
- data/.travis/Gemfile +0 -13
- data/.travis.yml +0 -73
- data/.yardopts +0 -3
- data/Gemfile +0 -3
- 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/bors.toml +0 -14
- 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/makefile +0 -42
- data/redis.gemspec +0 -42
- data/test/bitpos_test.rb +0 -63
- data/test/blocking_commands_test.rb +0 -40
- data/test/client_test.rb +0 -59
- data/test/command_map_test.rb +0 -28
- data/test/commands_on_hashes_test.rb +0 -19
- data/test/commands_on_hyper_log_log_test.rb +0 -19
- data/test/commands_on_lists_test.rb +0 -18
- data/test/commands_on_sets_test.rb +0 -75
- data/test/commands_on_sorted_sets_test.rb +0 -150
- data/test/commands_on_strings_test.rb +0 -99
- data/test/commands_on_value_types_test.rb +0 -171
- data/test/connection_handling_test.rb +0 -275
- data/test/connection_test.rb +0 -57
- data/test/db/.gitkeep +0 -0
- data/test/distributed_blocking_commands_test.rb +0 -44
- data/test/distributed_commands_on_hashes_test.rb +0 -8
- data/test/distributed_commands_on_hyper_log_log_test.rb +0 -31
- data/test/distributed_commands_on_lists_test.rb +0 -20
- data/test/distributed_commands_on_sets_test.rb +0 -106
- data/test/distributed_commands_on_sorted_sets_test.rb +0 -16
- data/test/distributed_commands_on_strings_test.rb +0 -69
- data/test/distributed_commands_on_value_types_test.rb +0 -93
- data/test/distributed_commands_requiring_clustering_test.rb +0 -162
- data/test/distributed_connection_handling_test.rb +0 -21
- data/test/distributed_internals_test.rb +0 -68
- data/test/distributed_key_tags_test.rb +0 -50
- data/test/distributed_persistence_control_commands_test.rb +0 -24
- data/test/distributed_publish_subscribe_test.rb +0 -90
- data/test/distributed_remote_server_control_commands_test.rb +0 -64
- data/test/distributed_scripting_test.rb +0 -100
- data/test/distributed_sorting_test.rb +0 -18
- data/test/distributed_test.rb +0 -56
- data/test/distributed_transactions_test.rb +0 -30
- data/test/encoding_test.rb +0 -14
- data/test/error_replies_test.rb +0 -57
- data/test/fork_safety_test.rb +0 -60
- data/test/helper.rb +0 -201
- data/test/helper_test.rb +0 -22
- data/test/internals_test.rb +0 -389
- 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 -246
- data/test/lint/value_types.rb +0 -130
- data/test/persistence_control_commands_test.rb +0 -24
- data/test/pipelining_commands_test.rb +0 -238
- data/test/publish_subscribe_test.rb +0 -280
- data/test/remote_server_control_commands_test.rb +0 -175
- data/test/scanning_test.rb +0 -407
- data/test/scripting_test.rb +0 -76
- data/test/sentinel_command_test.rb +0 -78
- data/test/sentinel_test.rb +0 -253
- data/test/sorting_test.rb +0 -57
- data/test/ssl_test.rb +0 -69
- 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 -85
- data/test/test.conf.erb +0 -9
- data/test/thread_safety_test.rb +0 -60
- data/test/transactions_test.rb +0 -262
- data/test/unknown_commands_test.rb +0 -12
- data/test/url_param_test.rb +0 -136
data/lib/redis/distributed.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
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.
|
@@ -109,13 +115,13 @@ class Redis
|
|
109
115
|
end
|
110
116
|
|
111
117
|
# Set a key's time to live in seconds.
|
112
|
-
def expire(key, seconds)
|
113
|
-
node_for(key).expire(key, seconds)
|
118
|
+
def expire(key, seconds, **kwargs)
|
119
|
+
node_for(key).expire(key, seconds, **kwargs)
|
114
120
|
end
|
115
121
|
|
116
122
|
# Set the expiration for a key as a UNIX timestamp.
|
117
|
-
def expireat(key, unix_time)
|
118
|
-
node_for(key).expireat(key, unix_time)
|
123
|
+
def expireat(key, unix_time, **kwargs)
|
124
|
+
node_for(key).expireat(key, unix_time, **kwargs)
|
119
125
|
end
|
120
126
|
|
121
127
|
# Get the time to live (in seconds) for a key.
|
@@ -124,13 +130,13 @@ class Redis
|
|
124
130
|
end
|
125
131
|
|
126
132
|
# Set a key's time to live in milliseconds.
|
127
|
-
def pexpire(key, milliseconds)
|
128
|
-
node_for(key).pexpire(key, milliseconds)
|
133
|
+
def pexpire(key, milliseconds, **kwarg)
|
134
|
+
node_for(key).pexpire(key, milliseconds, **kwarg)
|
129
135
|
end
|
130
136
|
|
131
137
|
# Set the expiration for a key as number of milliseconds from UNIX Epoch.
|
132
|
-
def pexpireat(key, ms_unix_time)
|
133
|
-
node_for(key).pexpireat(key, ms_unix_time)
|
138
|
+
def pexpireat(key, ms_unix_time, **kwarg)
|
139
|
+
node_for(key).pexpireat(key, ms_unix_time, **kwarg)
|
134
140
|
end
|
135
141
|
|
136
142
|
# Get the time to live (in milliseconds) for a key.
|
@@ -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, options
|
148
|
-
node_for(key).restore(key, ttl, serialized_value, options)
|
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,6 +319,16 @@ class Redis
|
|
277
319
|
node_for(key).get(key)
|
278
320
|
end
|
279
321
|
|
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
|
+
|
280
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)
|
@@ -327,7 +379,7 @@ class Redis
|
|
327
379
|
end
|
328
380
|
|
329
381
|
# Return the position of the first bit set to 1 or 0 in a string.
|
330
|
-
def bitpos(key, bit, start=nil, stop=nil)
|
382
|
+
def bitpos(key, bit, start = nil, stop = nil)
|
331
383
|
node_for(key).bitpos(key, bit, start, stop)
|
332
384
|
end
|
333
385
|
|
@@ -345,7 +397,7 @@ class Redis
|
|
345
397
|
get(key)
|
346
398
|
end
|
347
399
|
|
348
|
-
def []=(key,value)
|
400
|
+
def []=(key, value)
|
349
401
|
set(key, value)
|
350
402
|
end
|
351
403
|
|
@@ -354,6 +406,21 @@ class Redis
|
|
354
406
|
node_for(key).llen(key)
|
355
407
|
end
|
356
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
|
+
|
357
424
|
# Prepend one or more values to a list.
|
358
425
|
def lpush(key, value)
|
359
426
|
node_for(key).lpush(key, value)
|
@@ -374,14 +441,14 @@ class Redis
|
|
374
441
|
node_for(key).rpushx(key, value)
|
375
442
|
end
|
376
443
|
|
377
|
-
# Remove and get the first
|
378
|
-
def lpop(key)
|
379
|
-
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)
|
380
447
|
end
|
381
448
|
|
382
|
-
# Remove and get the last
|
383
|
-
def rpop(key)
|
384
|
-
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)
|
385
452
|
end
|
386
453
|
|
387
454
|
# Remove the last element in a list, append it to another list and return
|
@@ -393,24 +460,27 @@ class Redis
|
|
393
460
|
end
|
394
461
|
|
395
462
|
def _bpop(cmd, args)
|
396
|
-
|
397
|
-
|
398
|
-
case args.last
|
399
|
-
when Hash
|
463
|
+
timeout = if args.last.is_a?(Hash)
|
400
464
|
options = args.pop
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
465
|
+
options[:timeout]
|
466
|
+
elsif args.last.respond_to?(:to_int)
|
467
|
+
last_arg = args.pop
|
468
|
+
::Redis.deprecate!(
|
469
|
+
"Passing the timeout as a positional argument is deprecated, it should be passed as a keyword argument:\n" \
|
470
|
+
" redis.#{cmd}(#{args.map(&:inspect).join(', ')}, timeout: #{last_arg.to_int})" \
|
471
|
+
"(called from: #{caller(2, 1).first})"
|
472
|
+
)
|
473
|
+
last_arg.to_int
|
408
474
|
end
|
409
475
|
|
410
476
|
keys = args.flatten
|
411
477
|
|
412
478
|
ensure_same_node(cmd, keys) do |node|
|
413
|
-
|
479
|
+
if timeout
|
480
|
+
node.__send__(cmd, keys, timeout: timeout)
|
481
|
+
else
|
482
|
+
node.__send__(cmd, keys)
|
483
|
+
end
|
414
484
|
end
|
415
485
|
end
|
416
486
|
|
@@ -428,15 +498,9 @@ class Redis
|
|
428
498
|
|
429
499
|
# Pop a value from a list, push it to another list and return it; or block
|
430
500
|
# until one is available.
|
431
|
-
def brpoplpush(source, destination,
|
432
|
-
case options
|
433
|
-
when Integer
|
434
|
-
# Issue deprecation notice in obnoxious mode...
|
435
|
-
options = { :timeout => options }
|
436
|
-
end
|
437
|
-
|
501
|
+
def brpoplpush(source, destination, deprecated_timeout = 0, **options)
|
438
502
|
ensure_same_node(:brpoplpush, [source, destination]) do |node|
|
439
|
-
node.brpoplpush(source, destination, options)
|
503
|
+
node.brpoplpush(source, destination, deprecated_timeout, **options)
|
440
504
|
end
|
441
505
|
end
|
442
506
|
|
@@ -480,11 +544,21 @@ class Redis
|
|
480
544
|
node_for(key).sadd(key, member)
|
481
545
|
end
|
482
546
|
|
547
|
+
# Add one or more members to a set.
|
548
|
+
def sadd?(key, member)
|
549
|
+
node_for(key).sadd?(key, member)
|
550
|
+
end
|
551
|
+
|
483
552
|
# Remove one or more members from a set.
|
484
553
|
def srem(key, member)
|
485
554
|
node_for(key).srem(key, member)
|
486
555
|
end
|
487
556
|
|
557
|
+
# Remove one or more members from a set.
|
558
|
+
def srem?(key, member)
|
559
|
+
node_for(key).srem?(key, member)
|
560
|
+
end
|
561
|
+
|
488
562
|
# Remove and return a random member from a set.
|
489
563
|
def spop(key, count = nil)
|
490
564
|
node_for(key).spop(key, count)
|
@@ -507,19 +581,24 @@ class Redis
|
|
507
581
|
node_for(key).sismember(key, member)
|
508
582
|
end
|
509
583
|
|
584
|
+
# Determine if multiple values are members of a set.
|
585
|
+
def smismember(key, *members)
|
586
|
+
node_for(key).smismember(key, *members)
|
587
|
+
end
|
588
|
+
|
510
589
|
# Get all the members in a set.
|
511
590
|
def smembers(key)
|
512
591
|
node_for(key).smembers(key)
|
513
592
|
end
|
514
593
|
|
515
594
|
# Scan a set
|
516
|
-
def sscan(key, cursor, options
|
517
|
-
node_for(key).sscan(key, cursor, options)
|
595
|
+
def sscan(key, cursor, **options)
|
596
|
+
node_for(key).sscan(key, cursor, **options)
|
518
597
|
end
|
519
598
|
|
520
599
|
# Scan a set and return an enumerator
|
521
|
-
def sscan_each(key, options
|
522
|
-
node_for(key).sscan_each(key, options, &block)
|
600
|
+
def sscan_each(key, **options, &block)
|
601
|
+
node_for(key).sscan_each(key, **options, &block)
|
523
602
|
end
|
524
603
|
|
525
604
|
# Subtract multiple sets.
|
@@ -574,6 +653,7 @@ class Redis
|
|
574
653
|
def zadd(key, *args)
|
575
654
|
node_for(key).zadd(key, *args)
|
576
655
|
end
|
656
|
+
ruby2_keywords(:zadd) if respond_to?(:ruby2_keywords, true)
|
577
657
|
|
578
658
|
# Increment the score of a member in a sorted set.
|
579
659
|
def zincrby(key, increment, member)
|
@@ -590,15 +670,33 @@ class Redis
|
|
590
670
|
node_for(key).zscore(key, member)
|
591
671
|
end
|
592
672
|
|
593
|
-
#
|
594
|
-
def
|
595
|
-
node_for(key).
|
673
|
+
# Get one or more random members from a sorted set.
|
674
|
+
def zrandmember(key, count = nil, **options)
|
675
|
+
node_for(key).zrandmember(key, count, **options)
|
676
|
+
end
|
677
|
+
|
678
|
+
# Get the scores associated with the given members in a sorted set.
|
679
|
+
def zmscore(key, *members)
|
680
|
+
node_for(key).zmscore(key, *members)
|
681
|
+
end
|
682
|
+
|
683
|
+
# Return a range of members in a sorted set, by index, score or lexicographical ordering.
|
684
|
+
def zrange(key, start, stop, **options)
|
685
|
+
node_for(key).zrange(key, start, stop, **options)
|
686
|
+
end
|
687
|
+
|
688
|
+
# Select a range of members in a sorted set, by index, score or lexicographical ordering
|
689
|
+
# and store the resulting sorted set in a new key.
|
690
|
+
def zrangestore(dest_key, src_key, start, stop, **options)
|
691
|
+
ensure_same_node(:zrangestore, [dest_key, src_key]) do |node|
|
692
|
+
node.zrangestore(dest_key, src_key, start, stop, **options)
|
693
|
+
end
|
596
694
|
end
|
597
695
|
|
598
696
|
# Return a range of members in a sorted set, by index, with scores ordered
|
599
697
|
# from high to low.
|
600
|
-
def zrevrange(key, start, stop, options
|
601
|
-
node_for(key).zrevrange(key, start, stop, options)
|
698
|
+
def zrevrange(key, start, stop, **options)
|
699
|
+
node_for(key).zrevrange(key, start, stop, **options)
|
602
700
|
end
|
603
701
|
|
604
702
|
# Determine the index of a member in a sorted set.
|
@@ -618,14 +716,14 @@ class Redis
|
|
618
716
|
end
|
619
717
|
|
620
718
|
# Return a range of members in a sorted set, by score.
|
621
|
-
def zrangebyscore(key, min, max, options
|
622
|
-
node_for(key).zrangebyscore(key, min, max, options)
|
719
|
+
def zrangebyscore(key, min, max, **options)
|
720
|
+
node_for(key).zrangebyscore(key, min, max, **options)
|
623
721
|
end
|
624
722
|
|
625
723
|
# Return a range of members in a sorted set, by score, with scores ordered
|
626
724
|
# from high to low.
|
627
|
-
def zrevrangebyscore(key, max, min, options
|
628
|
-
node_for(key).zrevrangebyscore(key, max, min, options)
|
725
|
+
def zrevrangebyscore(key, max, min, **options)
|
726
|
+
node_for(key).zrevrangebyscore(key, max, min, **options)
|
629
727
|
end
|
630
728
|
|
631
729
|
# Remove all members in a sorted set within the given scores.
|
@@ -638,18 +736,47 @@ class Redis
|
|
638
736
|
node_for(key).zcount(key, min, max)
|
639
737
|
end
|
640
738
|
|
739
|
+
# Get the intersection of multiple sorted sets
|
740
|
+
def zinter(*keys, **options)
|
741
|
+
ensure_same_node(:zinter, keys) do |node|
|
742
|
+
node.zinter(*keys, **options)
|
743
|
+
end
|
744
|
+
end
|
745
|
+
|
641
746
|
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
642
747
|
# key.
|
643
|
-
def zinterstore(destination, keys, options
|
748
|
+
def zinterstore(destination, keys, **options)
|
644
749
|
ensure_same_node(:zinterstore, [destination] + keys) do |node|
|
645
|
-
node.zinterstore(destination, keys, options)
|
750
|
+
node.zinterstore(destination, keys, **options)
|
751
|
+
end
|
752
|
+
end
|
753
|
+
|
754
|
+
# Return the union of multiple sorted sets.
|
755
|
+
def zunion(*keys, **options)
|
756
|
+
ensure_same_node(:zunion, keys) do |node|
|
757
|
+
node.zunion(*keys, **options)
|
646
758
|
end
|
647
759
|
end
|
648
760
|
|
649
761
|
# Add multiple sorted sets and store the resulting sorted set in a new key.
|
650
|
-
def zunionstore(destination, keys, options
|
762
|
+
def zunionstore(destination, keys, **options)
|
651
763
|
ensure_same_node(:zunionstore, [destination] + keys) do |node|
|
652
|
-
node.zunionstore(destination, keys, options)
|
764
|
+
node.zunionstore(destination, keys, **options)
|
765
|
+
end
|
766
|
+
end
|
767
|
+
|
768
|
+
# Return the difference between the first and all successive input sorted sets.
|
769
|
+
def zdiff(*keys, **options)
|
770
|
+
ensure_same_node(:zdiff, keys) do |node|
|
771
|
+
node.zdiff(*keys, **options)
|
772
|
+
end
|
773
|
+
end
|
774
|
+
|
775
|
+
# Compute the difference between the first and all successive input sorted sets
|
776
|
+
# and store the resulting sorted set in a new key.
|
777
|
+
def zdiffstore(destination, keys, **options)
|
778
|
+
ensure_same_node(:zdiffstore, [destination] + keys) do |node|
|
779
|
+
node.zdiffstore(destination, keys, **options)
|
653
780
|
end
|
654
781
|
end
|
655
782
|
|
@@ -658,9 +785,9 @@ class Redis
|
|
658
785
|
node_for(key).hlen(key)
|
659
786
|
end
|
660
787
|
|
661
|
-
# Set
|
662
|
-
def hset(key,
|
663
|
-
node_for(key).hset(key,
|
788
|
+
# Set multiple hash fields to multiple values.
|
789
|
+
def hset(key, *attrs)
|
790
|
+
node_for(key).hset(key, *attrs)
|
664
791
|
end
|
665
792
|
|
666
793
|
# Set the value of a hash field, only if the field does not exist.
|
@@ -691,9 +818,13 @@ class Redis
|
|
691
818
|
Hash[*fields.zip(hmget(key, *fields)).flatten]
|
692
819
|
end
|
693
820
|
|
821
|
+
def hrandfield(key, count = nil, **options)
|
822
|
+
node_for(key).hrandfield(key, count, **options)
|
823
|
+
end
|
824
|
+
|
694
825
|
# Delete one or more hash fields.
|
695
|
-
def hdel(key,
|
696
|
-
node_for(key).hdel(key,
|
826
|
+
def hdel(key, *fields)
|
827
|
+
node_for(key).hdel(key, *fields)
|
697
828
|
end
|
698
829
|
|
699
830
|
# Determine if a hash field exists.
|
@@ -732,7 +863,7 @@ class Redis
|
|
732
863
|
end
|
733
864
|
|
734
865
|
def subscribed?
|
735
|
-
|
866
|
+
!!@subscribed_node
|
736
867
|
end
|
737
868
|
|
738
869
|
# Listen for messages published to the given channels.
|
@@ -750,7 +881,8 @@ class Redis
|
|
750
881
|
|
751
882
|
# Stop listening for messages posted to the given channels.
|
752
883
|
def unsubscribe(*channels)
|
753
|
-
raise
|
884
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
885
|
+
|
754
886
|
@subscribed_node.unsubscribe(*channels)
|
755
887
|
end
|
756
888
|
|
@@ -766,13 +898,26 @@ class Redis
|
|
766
898
|
end
|
767
899
|
|
768
900
|
# Watch the given keys to determine execution of the MULTI/EXEC block.
|
769
|
-
def watch(*keys)
|
770
|
-
|
901
|
+
def watch(*keys, &block)
|
902
|
+
ensure_same_node(:watch, keys) do |node|
|
903
|
+
@watch_key = key_tag(keys.first) || keys.first.to_s
|
904
|
+
|
905
|
+
begin
|
906
|
+
node.watch(*keys, &block)
|
907
|
+
rescue StandardError
|
908
|
+
@watch_key = nil
|
909
|
+
raise
|
910
|
+
end
|
911
|
+
end
|
771
912
|
end
|
772
913
|
|
773
914
|
# Forget about all watched keys.
|
774
915
|
def unwatch
|
775
|
-
raise CannotDistribute, :unwatch
|
916
|
+
raise CannotDistribute, :unwatch unless @watch_key
|
917
|
+
|
918
|
+
result = node_for(@watch_key).unwatch
|
919
|
+
@watch_key = nil
|
920
|
+
result
|
776
921
|
end
|
777
922
|
|
778
923
|
def pipelined
|
@@ -780,18 +925,30 @@ class Redis
|
|
780
925
|
end
|
781
926
|
|
782
927
|
# Mark the start of a transaction block.
|
783
|
-
def multi
|
784
|
-
raise CannotDistribute, :multi
|
928
|
+
def multi(&block)
|
929
|
+
raise CannotDistribute, :multi unless @watch_key
|
930
|
+
|
931
|
+
result = node_for(@watch_key).multi(&block)
|
932
|
+
@watch_key = nil if block_given?
|
933
|
+
result
|
785
934
|
end
|
786
935
|
|
787
936
|
# Execute all commands issued after MULTI.
|
788
937
|
def exec
|
789
|
-
raise CannotDistribute, :exec
|
938
|
+
raise CannotDistribute, :exec unless @watch_key
|
939
|
+
|
940
|
+
result = node_for(@watch_key).exec
|
941
|
+
@watch_key = nil
|
942
|
+
result
|
790
943
|
end
|
791
944
|
|
792
945
|
# Discard all commands issued after MULTI.
|
793
946
|
def discard
|
794
|
-
raise CannotDistribute, :discard
|
947
|
+
raise CannotDistribute, :discard unless @watch_key
|
948
|
+
|
949
|
+
result = node_for(@watch_key).discard
|
950
|
+
@watch_key = nil
|
951
|
+
result
|
795
952
|
end
|
796
953
|
|
797
954
|
# Control remote script registry.
|
@@ -850,7 +1007,7 @@ class Redis
|
|
850
1007
|
self.class.new(@node_configs, @default_options)
|
851
1008
|
end
|
852
1009
|
|
853
|
-
|
1010
|
+
protected
|
854
1011
|
|
855
1012
|
def on_each_node(command, *args)
|
856
1013
|
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
|