redis 3.0.0 → 4.2.2
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 +269 -0
- data/README.md +295 -58
- data/lib/redis.rb +1760 -451
- data/lib/redis/client.rb +355 -88
- data/lib/redis/cluster.rb +295 -0
- data/lib/redis/cluster/command.rb +81 -0
- data/lib/redis/cluster/command_loader.rb +34 -0
- data/lib/redis/cluster/key_slot_converter.rb +72 -0
- data/lib/redis/cluster/node.rb +107 -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 +90 -0
- data/lib/redis/cluster/slot.rb +86 -0
- data/lib/redis/cluster/slot_loader.rb +49 -0
- data/lib/redis/connection.rb +4 -2
- data/lib/redis/connection/command_helper.rb +5 -10
- data/lib/redis/connection/hiredis.rb +12 -8
- data/lib/redis/connection/registry.rb +2 -1
- data/lib/redis/connection/ruby.rb +232 -63
- data/lib/redis/connection/synchrony.rb +41 -14
- data/lib/redis/distributed.rb +205 -70
- 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
- metadata +63 -160
- data/.gitignore +0 -10
- data/.order +0 -169
- data/.travis.yml +0 -50
- data/.travis/Gemfile +0 -11
- 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
@@ -1,14 +1,23 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "command_helper"
|
4
|
+
require_relative "registry"
|
5
|
+
require_relative "../errors"
|
4
6
|
require "em-synchrony"
|
5
7
|
require "hiredis/reader"
|
6
8
|
|
9
|
+
Kernel.warn(
|
10
|
+
"The redis synchrony driver is deprecated and will be removed in redis-rb 5.0. " \
|
11
|
+
"We're looking for people to maintain it as a separate gem, see https://github.com/redis/redis-rb/issues/915"
|
12
|
+
)
|
13
|
+
|
7
14
|
class Redis
|
8
15
|
module Connection
|
9
16
|
class RedisClient < EventMachine::Connection
|
10
17
|
include EventMachine::Deferrable
|
11
18
|
|
19
|
+
attr_accessor :timeout
|
20
|
+
|
12
21
|
def post_init
|
13
22
|
@req = nil
|
14
23
|
@connected = false
|
@@ -27,18 +36,24 @@ class Redis
|
|
27
36
|
def receive_data(data)
|
28
37
|
@reader.feed(data)
|
29
38
|
|
30
|
-
|
31
|
-
|
32
|
-
reply =
|
33
|
-
|
39
|
+
loop do
|
40
|
+
begin
|
41
|
+
reply = @reader.gets
|
42
|
+
rescue RuntimeError => err
|
43
|
+
@req.fail [:error, ProtocolError.new(err.message)]
|
44
|
+
break
|
34
45
|
end
|
35
|
-
|
36
|
-
|
46
|
+
|
47
|
+
break if reply == false
|
48
|
+
|
49
|
+
reply = CommandError.new(reply.message) if reply.is_a?(RuntimeError)
|
50
|
+
@req.succeed [:reply, reply]
|
37
51
|
end
|
38
52
|
end
|
39
53
|
|
40
54
|
def read
|
41
55
|
@req = EventMachine::DefaultDeferrable.new
|
56
|
+
@req.timeout(@timeout, :timeout) if @timeout > 0
|
42
57
|
EventMachine::Synchrony.sync @req
|
43
58
|
end
|
44
59
|
|
@@ -62,10 +77,20 @@ class Redis
|
|
62
77
|
|
63
78
|
def self.connect(config)
|
64
79
|
if config[:scheme] == "unix"
|
65
|
-
|
80
|
+
begin
|
81
|
+
conn = EventMachine.connect_unix_domain(config[:path], RedisClient)
|
82
|
+
rescue RuntimeError => e
|
83
|
+
if e.message == "no connection"
|
84
|
+
raise Errno::ECONNREFUSED
|
85
|
+
else
|
86
|
+
raise e
|
87
|
+
end
|
88
|
+
end
|
89
|
+
elsif config[:scheme] == "rediss" || config[:ssl]
|
90
|
+
raise NotImplementedError, "SSL not supported by synchrony driver"
|
66
91
|
else
|
67
92
|
conn = EventMachine.connect(config[:host], config[:port], RedisClient) do |c|
|
68
|
-
c.pending_connect_timeout = [config[:
|
93
|
+
c.pending_connect_timeout = [config[:connect_timeout], 0.1].max
|
69
94
|
end
|
70
95
|
end
|
71
96
|
|
@@ -76,7 +101,7 @@ class Redis
|
|
76
101
|
raise Errno::ECONNREFUSED if Fiber.yield == :refused
|
77
102
|
|
78
103
|
instance = new(conn)
|
79
|
-
instance.timeout = config[:
|
104
|
+
instance.timeout = config[:read_timeout]
|
80
105
|
instance
|
81
106
|
end
|
82
107
|
|
@@ -85,11 +110,11 @@ class Redis
|
|
85
110
|
end
|
86
111
|
|
87
112
|
def connected?
|
88
|
-
@connection
|
113
|
+
@connection&.connected?
|
89
114
|
end
|
90
115
|
|
91
116
|
def timeout=(timeout)
|
92
|
-
@timeout = timeout
|
117
|
+
@connection.timeout = timeout
|
93
118
|
end
|
94
119
|
|
95
120
|
def disconnect
|
@@ -108,6 +133,8 @@ class Redis
|
|
108
133
|
payload
|
109
134
|
elsif type == :error
|
110
135
|
raise payload
|
136
|
+
elsif type == :timeout
|
137
|
+
raise TimeoutError
|
111
138
|
else
|
112
139
|
raise "Unknown type #{type.inspect}"
|
113
140
|
end
|
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,16 @@ class Redis
|
|
258
316
|
node_for(key).get(key)
|
259
317
|
end
|
260
318
|
|
261
|
-
# Get the values of all the given keys.
|
319
|
+
# Get the values of all the given keys as an Array.
|
262
320
|
def mget(*keys)
|
263
|
-
|
321
|
+
mapped_mget(*keys).values_at(*keys)
|
264
322
|
end
|
265
323
|
|
324
|
+
# Get the values of all the given keys as a Hash.
|
266
325
|
def mapped_mget(*keys)
|
267
|
-
|
326
|
+
keys.group_by { |k| node_for k }.inject({}) do |results, (node, subkeys)|
|
327
|
+
results.merge! node.mapped_mget(*subkeys)
|
328
|
+
end
|
268
329
|
end
|
269
330
|
|
270
331
|
# Overwrite part of a string at key starting at the specified offset.
|
@@ -292,6 +353,23 @@ class Redis
|
|
292
353
|
node_for(key).append(key, value)
|
293
354
|
end
|
294
355
|
|
356
|
+
# Count the number of set bits in a range of the string value stored at key.
|
357
|
+
def bitcount(key, start = 0, stop = -1)
|
358
|
+
node_for(key).bitcount(key, start, stop)
|
359
|
+
end
|
360
|
+
|
361
|
+
# Perform a bitwise operation between strings and store the resulting string in a key.
|
362
|
+
def bitop(operation, destkey, *keys)
|
363
|
+
ensure_same_node(:bitop, [destkey] + keys) do |node|
|
364
|
+
node.bitop(operation, destkey, *keys)
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
# Return the position of the first bit set to 1 or 0 in a string.
|
369
|
+
def bitpos(key, bit, start = nil, stop = nil)
|
370
|
+
node_for(key).bitpos(key, bit, start, stop)
|
371
|
+
end
|
372
|
+
|
295
373
|
# Set the string value of a key and return its old value.
|
296
374
|
def getset(key, value)
|
297
375
|
node_for(key).getset(key, value)
|
@@ -306,7 +384,7 @@ class Redis
|
|
306
384
|
get(key)
|
307
385
|
end
|
308
386
|
|
309
|
-
def []=(key,value)
|
387
|
+
def []=(key, value)
|
310
388
|
set(key, value)
|
311
389
|
end
|
312
390
|
|
@@ -354,14 +432,12 @@ class Redis
|
|
354
432
|
end
|
355
433
|
|
356
434
|
def _bpop(cmd, args)
|
357
|
-
|
358
|
-
|
359
|
-
case args.last
|
360
|
-
when Hash
|
435
|
+
timeout = if args.last.is_a?(Hash)
|
361
436
|
options = args.pop
|
362
|
-
|
437
|
+
options[:timeout]
|
438
|
+
elsif args.last.respond_to?(:to_int)
|
363
439
|
# Issue deprecation notice in obnoxious mode...
|
364
|
-
|
440
|
+
args.pop.to_int
|
365
441
|
end
|
366
442
|
|
367
443
|
if args.size > 1
|
@@ -371,7 +447,11 @@ class Redis
|
|
371
447
|
keys = args.flatten
|
372
448
|
|
373
449
|
ensure_same_node(cmd, keys) do |node|
|
374
|
-
|
450
|
+
if timeout
|
451
|
+
node.__send__(cmd, keys, timeout: timeout)
|
452
|
+
else
|
453
|
+
node.__send__(cmd, keys)
|
454
|
+
end
|
375
455
|
end
|
376
456
|
end
|
377
457
|
|
@@ -389,15 +469,9 @@ class Redis
|
|
389
469
|
|
390
470
|
# Pop a value from a list, push it to another list and return it; or block
|
391
471
|
# 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
|
-
|
472
|
+
def brpoplpush(source, destination, deprecated_timeout = 0, **options)
|
399
473
|
ensure_same_node(:brpoplpush, [source, destination]) do |node|
|
400
|
-
node.brpoplpush(source, destination, options)
|
474
|
+
node.brpoplpush(source, destination, deprecated_timeout, **options)
|
401
475
|
end
|
402
476
|
end
|
403
477
|
|
@@ -447,13 +521,13 @@ class Redis
|
|
447
521
|
end
|
448
522
|
|
449
523
|
# Remove and return a random member from a set.
|
450
|
-
def spop(key)
|
451
|
-
node_for(key).spop(key)
|
524
|
+
def spop(key, count = nil)
|
525
|
+
node_for(key).spop(key, count)
|
452
526
|
end
|
453
527
|
|
454
528
|
# Get a random member from a set.
|
455
|
-
def srandmember(key)
|
456
|
-
node_for(key).srandmember(key)
|
529
|
+
def srandmember(key, count = nil)
|
530
|
+
node_for(key).srandmember(key, count)
|
457
531
|
end
|
458
532
|
|
459
533
|
# Move a member from one set to another.
|
@@ -473,6 +547,16 @@ class Redis
|
|
473
547
|
node_for(key).smembers(key)
|
474
548
|
end
|
475
549
|
|
550
|
+
# Scan a set
|
551
|
+
def sscan(key, cursor, **options)
|
552
|
+
node_for(key).sscan(key, cursor, **options)
|
553
|
+
end
|
554
|
+
|
555
|
+
# Scan a set and return an enumerator
|
556
|
+
def sscan_each(key, **options, &block)
|
557
|
+
node_for(key).sscan_each(key, **options, &block)
|
558
|
+
end
|
559
|
+
|
476
560
|
# Subtract multiple sets.
|
477
561
|
def sdiff(*keys)
|
478
562
|
ensure_same_node(:sdiff, keys) do |node|
|
@@ -525,6 +609,7 @@ class Redis
|
|
525
609
|
def zadd(key, *args)
|
526
610
|
node_for(key).zadd(key, *args)
|
527
611
|
end
|
612
|
+
ruby2_keywords(:zadd) if respond_to?(:ruby2_keywords, true)
|
528
613
|
|
529
614
|
# Increment the score of a member in a sorted set.
|
530
615
|
def zincrby(key, increment, member)
|
@@ -542,14 +627,14 @@ class Redis
|
|
542
627
|
end
|
543
628
|
|
544
629
|
# 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)
|
630
|
+
def zrange(key, start, stop, **options)
|
631
|
+
node_for(key).zrange(key, start, stop, **options)
|
547
632
|
end
|
548
633
|
|
549
634
|
# Return a range of members in a sorted set, by index, with scores ordered
|
550
635
|
# from high to low.
|
551
|
-
def zrevrange(key, start, stop, options
|
552
|
-
node_for(key).zrevrange(key, start, stop, options)
|
636
|
+
def zrevrange(key, start, stop, **options)
|
637
|
+
node_for(key).zrevrange(key, start, stop, **options)
|
553
638
|
end
|
554
639
|
|
555
640
|
# Determine the index of a member in a sorted set.
|
@@ -569,14 +654,14 @@ class Redis
|
|
569
654
|
end
|
570
655
|
|
571
656
|
# 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)
|
657
|
+
def zrangebyscore(key, min, max, **options)
|
658
|
+
node_for(key).zrangebyscore(key, min, max, **options)
|
574
659
|
end
|
575
660
|
|
576
661
|
# Return a range of members in a sorted set, by score, with scores ordered
|
577
662
|
# from high to low.
|
578
|
-
def zrevrangebyscore(key, max, min, options
|
579
|
-
node_for(key).zrevrangebyscore(key, max, min, options)
|
663
|
+
def zrevrangebyscore(key, max, min, **options)
|
664
|
+
node_for(key).zrevrangebyscore(key, max, min, **options)
|
580
665
|
end
|
581
666
|
|
582
667
|
# Remove all members in a sorted set within the given scores.
|
@@ -591,16 +676,16 @@ class Redis
|
|
591
676
|
|
592
677
|
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
593
678
|
# key.
|
594
|
-
def zinterstore(destination, keys, options
|
679
|
+
def zinterstore(destination, keys, **options)
|
595
680
|
ensure_same_node(:zinterstore, [destination] + keys) do |node|
|
596
|
-
node.zinterstore(destination, keys, options)
|
681
|
+
node.zinterstore(destination, keys, **options)
|
597
682
|
end
|
598
683
|
end
|
599
684
|
|
600
685
|
# Add multiple sorted sets and store the resulting sorted set in a new key.
|
601
|
-
def zunionstore(destination, keys, options
|
686
|
+
def zunionstore(destination, keys, **options)
|
602
687
|
ensure_same_node(:zunionstore, [destination] + keys) do |node|
|
603
|
-
node.zunionstore(destination, keys, options)
|
688
|
+
node.zunionstore(destination, keys, **options)
|
604
689
|
end
|
605
690
|
end
|
606
691
|
|
@@ -609,9 +694,9 @@ class Redis
|
|
609
694
|
node_for(key).hlen(key)
|
610
695
|
end
|
611
696
|
|
612
|
-
# Set
|
613
|
-
def hset(key,
|
614
|
-
node_for(key).hset(key,
|
697
|
+
# Set multiple hash fields to multiple values.
|
698
|
+
def hset(key, *attrs)
|
699
|
+
node_for(key).hset(key, *attrs)
|
615
700
|
end
|
616
701
|
|
617
702
|
# Set the value of a hash field, only if the field does not exist.
|
@@ -643,8 +728,8 @@ class Redis
|
|
643
728
|
end
|
644
729
|
|
645
730
|
# Delete one or more hash fields.
|
646
|
-
def hdel(key,
|
647
|
-
node_for(key).hdel(key,
|
731
|
+
def hdel(key, *fields)
|
732
|
+
node_for(key).hdel(key, *fields)
|
648
733
|
end
|
649
734
|
|
650
735
|
# Determine if a hash field exists.
|
@@ -683,7 +768,7 @@ class Redis
|
|
683
768
|
end
|
684
769
|
|
685
770
|
def subscribed?
|
686
|
-
|
771
|
+
!!@subscribed_node
|
687
772
|
end
|
688
773
|
|
689
774
|
# Listen for messages published to the given channels.
|
@@ -701,7 +786,8 @@ class Redis
|
|
701
786
|
|
702
787
|
# Stop listening for messages posted to the given channels.
|
703
788
|
def unsubscribe(*channels)
|
704
|
-
raise
|
789
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
790
|
+
|
705
791
|
@subscribed_node.unsubscribe(*channels)
|
706
792
|
end
|
707
793
|
|
@@ -717,13 +803,26 @@ class Redis
|
|
717
803
|
end
|
718
804
|
|
719
805
|
# Watch the given keys to determine execution of the MULTI/EXEC block.
|
720
|
-
def watch(*keys)
|
721
|
-
|
806
|
+
def watch(*keys, &block)
|
807
|
+
ensure_same_node(:watch, keys) do |node|
|
808
|
+
@watch_key = key_tag(keys.first) || keys.first.to_s
|
809
|
+
|
810
|
+
begin
|
811
|
+
node.watch(*keys, &block)
|
812
|
+
rescue StandardError
|
813
|
+
@watch_key = nil
|
814
|
+
raise
|
815
|
+
end
|
816
|
+
end
|
722
817
|
end
|
723
818
|
|
724
819
|
# Forget about all watched keys.
|
725
820
|
def unwatch
|
726
|
-
raise CannotDistribute, :unwatch
|
821
|
+
raise CannotDistribute, :unwatch unless @watch_key
|
822
|
+
|
823
|
+
result = node_for(@watch_key).unwatch
|
824
|
+
@watch_key = nil
|
825
|
+
result
|
727
826
|
end
|
728
827
|
|
729
828
|
def pipelined
|
@@ -731,18 +830,30 @@ class Redis
|
|
731
830
|
end
|
732
831
|
|
733
832
|
# Mark the start of a transaction block.
|
734
|
-
def multi
|
735
|
-
raise CannotDistribute, :multi
|
833
|
+
def multi(&block)
|
834
|
+
raise CannotDistribute, :multi unless @watch_key
|
835
|
+
|
836
|
+
result = node_for(@watch_key).multi(&block)
|
837
|
+
@watch_key = nil if block_given?
|
838
|
+
result
|
736
839
|
end
|
737
840
|
|
738
841
|
# Execute all commands issued after MULTI.
|
739
842
|
def exec
|
740
|
-
raise CannotDistribute, :exec
|
843
|
+
raise CannotDistribute, :exec unless @watch_key
|
844
|
+
|
845
|
+
result = node_for(@watch_key).exec
|
846
|
+
@watch_key = nil
|
847
|
+
result
|
741
848
|
end
|
742
849
|
|
743
850
|
# Discard all commands issued after MULTI.
|
744
851
|
def discard
|
745
|
-
raise CannotDistribute, :discard
|
852
|
+
raise CannotDistribute, :discard unless @watch_key
|
853
|
+
|
854
|
+
result = node_for(@watch_key).discard
|
855
|
+
@watch_key = nil
|
856
|
+
result
|
746
857
|
end
|
747
858
|
|
748
859
|
# Control remote script registry.
|
@@ -750,6 +861,26 @@ class Redis
|
|
750
861
|
on_each_node(:script, subcommand, *args)
|
751
862
|
end
|
752
863
|
|
864
|
+
# Add one or more members to a HyperLogLog structure.
|
865
|
+
def pfadd(key, member)
|
866
|
+
node_for(key).pfadd(key, member)
|
867
|
+
end
|
868
|
+
|
869
|
+
# Get the approximate cardinality of members added to HyperLogLog structure.
|
870
|
+
def pfcount(*keys)
|
871
|
+
ensure_same_node(:pfcount, keys.flatten(1)) do |node|
|
872
|
+
node.pfcount(keys)
|
873
|
+
end
|
874
|
+
end
|
875
|
+
|
876
|
+
# Merge multiple HyperLogLog values into an unique value that will approximate the cardinality of the union of
|
877
|
+
# the observed Sets of the source HyperLogLog structures.
|
878
|
+
def pfmerge(dest_key, *source_key)
|
879
|
+
ensure_same_node(:pfmerge, [dest_key, *source_key]) do |node|
|
880
|
+
node.pfmerge(dest_key, *source_key)
|
881
|
+
end
|
882
|
+
end
|
883
|
+
|
753
884
|
def _eval(cmd, args)
|
754
885
|
script = args.shift
|
755
886
|
options = args.pop if args.last.is_a?(Hash)
|
@@ -777,7 +908,11 @@ class Redis
|
|
777
908
|
"#<Redis client v#{Redis::VERSION} for #{nodes.map(&:id).join(', ')}>"
|
778
909
|
end
|
779
910
|
|
780
|
-
|
911
|
+
def dup
|
912
|
+
self.class.new(@node_configs, @default_options)
|
913
|
+
end
|
914
|
+
|
915
|
+
protected
|
781
916
|
|
782
917
|
def on_each_node(command, *args)
|
783
918
|
nodes.map do |node|
|