redis 3.3.5 → 4.8.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 +232 -2
- data/README.md +169 -89
- data/lib/redis/client.rb +177 -100
- 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 +7 -10
- data/lib/redis/connection/hiredis.rb +5 -3
- data/lib/redis/connection/registry.rb +2 -1
- data/lib/redis/connection/ruby.rb +136 -128
- data/lib/redis/connection/synchrony.rb +24 -9
- data/lib/redis/connection.rb +3 -1
- data/lib/redis/distributed.rb +255 -85
- 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 +174 -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.
|
@@ -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)
|
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,24 +460,27 @@ 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
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
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
|
405
474
|
end
|
406
475
|
|
407
476
|
keys = args.flatten
|
408
477
|
|
409
478
|
ensure_same_node(cmd, keys) do |node|
|
410
|
-
|
479
|
+
if timeout
|
480
|
+
node.__send__(cmd, keys, timeout: timeout)
|
481
|
+
else
|
482
|
+
node.__send__(cmd, keys)
|
483
|
+
end
|
411
484
|
end
|
412
485
|
end
|
413
486
|
|
@@ -425,15 +498,9 @@ class Redis
|
|
425
498
|
|
426
499
|
# Pop a value from a list, push it to another list and return it; or block
|
427
500
|
# 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
|
-
|
501
|
+
def brpoplpush(source, destination, deprecated_timeout = 0, **options)
|
435
502
|
ensure_same_node(:brpoplpush, [source, destination]) do |node|
|
436
|
-
node.brpoplpush(source, destination, options)
|
503
|
+
node.brpoplpush(source, destination, deprecated_timeout, **options)
|
437
504
|
end
|
438
505
|
end
|
439
506
|
|
@@ -477,11 +544,21 @@ class Redis
|
|
477
544
|
node_for(key).sadd(key, member)
|
478
545
|
end
|
479
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
|
+
|
480
552
|
# Remove one or more members from a set.
|
481
553
|
def srem(key, member)
|
482
554
|
node_for(key).srem(key, member)
|
483
555
|
end
|
484
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
|
+
|
485
562
|
# Remove and return a random member from a set.
|
486
563
|
def spop(key, count = nil)
|
487
564
|
node_for(key).spop(key, count)
|
@@ -504,11 +581,26 @@ class Redis
|
|
504
581
|
node_for(key).sismember(key, member)
|
505
582
|
end
|
506
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
|
+
|
507
589
|
# Get all the members in a set.
|
508
590
|
def smembers(key)
|
509
591
|
node_for(key).smembers(key)
|
510
592
|
end
|
511
593
|
|
594
|
+
# Scan a set
|
595
|
+
def sscan(key, cursor, **options)
|
596
|
+
node_for(key).sscan(key, cursor, **options)
|
597
|
+
end
|
598
|
+
|
599
|
+
# Scan a set and return an enumerator
|
600
|
+
def sscan_each(key, **options, &block)
|
601
|
+
node_for(key).sscan_each(key, **options, &block)
|
602
|
+
end
|
603
|
+
|
512
604
|
# Subtract multiple sets.
|
513
605
|
def sdiff(*keys)
|
514
606
|
ensure_same_node(:sdiff, keys) do |node|
|
@@ -561,6 +653,7 @@ class Redis
|
|
561
653
|
def zadd(key, *args)
|
562
654
|
node_for(key).zadd(key, *args)
|
563
655
|
end
|
656
|
+
ruby2_keywords(:zadd) if respond_to?(:ruby2_keywords, true)
|
564
657
|
|
565
658
|
# Increment the score of a member in a sorted set.
|
566
659
|
def zincrby(key, increment, member)
|
@@ -577,15 +670,33 @@ class Redis
|
|
577
670
|
node_for(key).zscore(key, member)
|
578
671
|
end
|
579
672
|
|
580
|
-
#
|
581
|
-
def
|
582
|
-
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
|
583
694
|
end
|
584
695
|
|
585
696
|
# Return a range of members in a sorted set, by index, with scores ordered
|
586
697
|
# from high to low.
|
587
|
-
def zrevrange(key, start, stop, options
|
588
|
-
node_for(key).zrevrange(key, start, stop, options)
|
698
|
+
def zrevrange(key, start, stop, **options)
|
699
|
+
node_for(key).zrevrange(key, start, stop, **options)
|
589
700
|
end
|
590
701
|
|
591
702
|
# Determine the index of a member in a sorted set.
|
@@ -605,14 +716,14 @@ class Redis
|
|
605
716
|
end
|
606
717
|
|
607
718
|
# 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)
|
719
|
+
def zrangebyscore(key, min, max, **options)
|
720
|
+
node_for(key).zrangebyscore(key, min, max, **options)
|
610
721
|
end
|
611
722
|
|
612
723
|
# Return a range of members in a sorted set, by score, with scores ordered
|
613
724
|
# from high to low.
|
614
|
-
def zrevrangebyscore(key, max, min, options
|
615
|
-
node_for(key).zrevrangebyscore(key, max, min, options)
|
725
|
+
def zrevrangebyscore(key, max, min, **options)
|
726
|
+
node_for(key).zrevrangebyscore(key, max, min, **options)
|
616
727
|
end
|
617
728
|
|
618
729
|
# Remove all members in a sorted set within the given scores.
|
@@ -625,18 +736,47 @@ class Redis
|
|
625
736
|
node_for(key).zcount(key, min, max)
|
626
737
|
end
|
627
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
|
+
|
628
746
|
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
629
747
|
# key.
|
630
|
-
def zinterstore(destination, keys, options
|
748
|
+
def zinterstore(destination, keys, **options)
|
631
749
|
ensure_same_node(:zinterstore, [destination] + keys) do |node|
|
632
|
-
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)
|
633
758
|
end
|
634
759
|
end
|
635
760
|
|
636
761
|
# Add multiple sorted sets and store the resulting sorted set in a new key.
|
637
|
-
def zunionstore(destination, keys, options
|
762
|
+
def zunionstore(destination, keys, **options)
|
638
763
|
ensure_same_node(:zunionstore, [destination] + keys) do |node|
|
639
|
-
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)
|
640
780
|
end
|
641
781
|
end
|
642
782
|
|
@@ -645,9 +785,9 @@ class Redis
|
|
645
785
|
node_for(key).hlen(key)
|
646
786
|
end
|
647
787
|
|
648
|
-
# Set
|
649
|
-
def hset(key,
|
650
|
-
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)
|
651
791
|
end
|
652
792
|
|
653
793
|
# Set the value of a hash field, only if the field does not exist.
|
@@ -678,9 +818,13 @@ class Redis
|
|
678
818
|
Hash[*fields.zip(hmget(key, *fields)).flatten]
|
679
819
|
end
|
680
820
|
|
821
|
+
def hrandfield(key, count = nil, **options)
|
822
|
+
node_for(key).hrandfield(key, count, **options)
|
823
|
+
end
|
824
|
+
|
681
825
|
# Delete one or more hash fields.
|
682
|
-
def hdel(key,
|
683
|
-
node_for(key).hdel(key,
|
826
|
+
def hdel(key, *fields)
|
827
|
+
node_for(key).hdel(key, *fields)
|
684
828
|
end
|
685
829
|
|
686
830
|
# Determine if a hash field exists.
|
@@ -719,7 +863,7 @@ class Redis
|
|
719
863
|
end
|
720
864
|
|
721
865
|
def subscribed?
|
722
|
-
|
866
|
+
!!@subscribed_node
|
723
867
|
end
|
724
868
|
|
725
869
|
# Listen for messages published to the given channels.
|
@@ -737,7 +881,8 @@ class Redis
|
|
737
881
|
|
738
882
|
# Stop listening for messages posted to the given channels.
|
739
883
|
def unsubscribe(*channels)
|
740
|
-
raise
|
884
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
885
|
+
|
741
886
|
@subscribed_node.unsubscribe(*channels)
|
742
887
|
end
|
743
888
|
|
@@ -753,13 +898,26 @@ class Redis
|
|
753
898
|
end
|
754
899
|
|
755
900
|
# Watch the given keys to determine execution of the MULTI/EXEC block.
|
756
|
-
def watch(*keys)
|
757
|
-
|
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
|
758
912
|
end
|
759
913
|
|
760
914
|
# Forget about all watched keys.
|
761
915
|
def unwatch
|
762
|
-
raise CannotDistribute, :unwatch
|
916
|
+
raise CannotDistribute, :unwatch unless @watch_key
|
917
|
+
|
918
|
+
result = node_for(@watch_key).unwatch
|
919
|
+
@watch_key = nil
|
920
|
+
result
|
763
921
|
end
|
764
922
|
|
765
923
|
def pipelined
|
@@ -767,18 +925,30 @@ class Redis
|
|
767
925
|
end
|
768
926
|
|
769
927
|
# Mark the start of a transaction block.
|
770
|
-
def multi
|
771
|
-
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
|
772
934
|
end
|
773
935
|
|
774
936
|
# Execute all commands issued after MULTI.
|
775
937
|
def exec
|
776
|
-
raise CannotDistribute, :exec
|
938
|
+
raise CannotDistribute, :exec unless @watch_key
|
939
|
+
|
940
|
+
result = node_for(@watch_key).exec
|
941
|
+
@watch_key = nil
|
942
|
+
result
|
777
943
|
end
|
778
944
|
|
779
945
|
# Discard all commands issued after MULTI.
|
780
946
|
def discard
|
781
|
-
raise CannotDistribute, :discard
|
947
|
+
raise CannotDistribute, :discard unless @watch_key
|
948
|
+
|
949
|
+
result = node_for(@watch_key).discard
|
950
|
+
@watch_key = nil
|
951
|
+
result
|
782
952
|
end
|
783
953
|
|
784
954
|
# Control remote script registry.
|
@@ -837,7 +1007,7 @@ class Redis
|
|
837
1007
|
self.class.new(@node_configs, @default_options)
|
838
1008
|
end
|
839
1009
|
|
840
|
-
|
1010
|
+
protected
|
841
1011
|
|
842
1012
|
def on_each_node(command, *args)
|
843
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
|