redis 4.1.0 → 4.6.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 +158 -0
- data/README.md +91 -27
- data/lib/redis/client.rb +148 -92
- data/lib/redis/cluster/command.rb +4 -6
- data/lib/redis/cluster/command_loader.rb +6 -7
- data/lib/redis/cluster/node.rb +17 -1
- data/lib/redis/cluster/node_key.rb +3 -7
- data/lib/redis/cluster/option.rb +30 -14
- data/lib/redis/cluster/slot.rb +30 -13
- data/lib/redis/cluster/slot_loader.rb +4 -4
- data/lib/redis/cluster.rb +46 -17
- 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 +804 -0
- data/lib/redis/commands/streams.rb +382 -0
- data/lib/redis/commands/strings.rb +313 -0
- data/lib/redis/commands/transactions.rb +92 -0
- data/lib/redis/commands.rb +242 -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 +129 -110
- data/lib/redis/connection/synchrony.rb +17 -10
- data/lib/redis/connection.rb +3 -1
- data/lib/redis/distributed.rb +209 -70
- data/lib/redis/errors.rb +2 -0
- data/lib/redis/hash_ring.rb +15 -14
- data/lib/redis/pipeline.rb +139 -8
- data/lib/redis/subscribe.rb +11 -12
- data/lib/redis/version.rb +3 -1
- data/lib/redis.rb +167 -3377
- metadata +32 -25
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.
|
@@ -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
|
|
@@ -170,8 +176,29 @@ class Redis
|
|
170
176
|
end
|
171
177
|
|
172
178
|
# Determine if a key exists.
|
173
|
-
def exists(
|
174
|
-
|
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
|
175
202
|
end
|
176
203
|
|
177
204
|
# Find all keys matching the given pattern.
|
@@ -184,6 +211,13 @@ class Redis
|
|
184
211
|
node_for(key).move(key, db)
|
185
212
|
end
|
186
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
|
+
|
187
221
|
# Return a random key from the keyspace.
|
188
222
|
def randomkey
|
189
223
|
raise CannotDistribute, :randomkey
|
@@ -204,11 +238,11 @@ class Redis
|
|
204
238
|
end
|
205
239
|
|
206
240
|
# Sort the elements in a list, set or sorted set.
|
207
|
-
def sort(key, options
|
241
|
+
def sort(key, **options)
|
208
242
|
keys = [key, options[:by], options[:store], *Array(options[:get])].compact
|
209
243
|
|
210
244
|
ensure_same_node(:sort, keys) do |node|
|
211
|
-
node.sort(key, options)
|
245
|
+
node.sort(key, **options)
|
212
246
|
end
|
213
247
|
end
|
214
248
|
|
@@ -243,8 +277,8 @@ class Redis
|
|
243
277
|
end
|
244
278
|
|
245
279
|
# Set the string value of a key.
|
246
|
-
def set(key, value, options
|
247
|
-
node_for(key).set(key, value, options)
|
280
|
+
def set(key, value, **options)
|
281
|
+
node_for(key).set(key, value, **options)
|
248
282
|
end
|
249
283
|
|
250
284
|
# Set the time to live in seconds of a key.
|
@@ -263,20 +297,20 @@ class Redis
|
|
263
297
|
end
|
264
298
|
|
265
299
|
# Set multiple keys to multiple values.
|
266
|
-
def mset(*
|
300
|
+
def mset(*_args)
|
267
301
|
raise CannotDistribute, :mset
|
268
302
|
end
|
269
303
|
|
270
|
-
def mapped_mset(
|
304
|
+
def mapped_mset(_hash)
|
271
305
|
raise CannotDistribute, :mapped_mset
|
272
306
|
end
|
273
307
|
|
274
308
|
# Set multiple keys to multiple values, only if none of the keys exist.
|
275
|
-
def msetnx(*
|
309
|
+
def msetnx(*_args)
|
276
310
|
raise CannotDistribute, :msetnx
|
277
311
|
end
|
278
312
|
|
279
|
-
def mapped_msetnx(
|
313
|
+
def mapped_msetnx(_hash)
|
280
314
|
raise CannotDistribute, :mapped_msetnx
|
281
315
|
end
|
282
316
|
|
@@ -285,6 +319,16 @@ class Redis
|
|
285
319
|
node_for(key).get(key)
|
286
320
|
end
|
287
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
|
+
|
288
332
|
# Get the values of all the given keys as an Array.
|
289
333
|
def mget(*keys)
|
290
334
|
mapped_mget(*keys).values_at(*keys)
|
@@ -335,7 +379,7 @@ class Redis
|
|
335
379
|
end
|
336
380
|
|
337
381
|
# Return the position of the first bit set to 1 or 0 in a string.
|
338
|
-
def bitpos(key, bit, start=nil, stop=nil)
|
382
|
+
def bitpos(key, bit, start = nil, stop = nil)
|
339
383
|
node_for(key).bitpos(key, bit, start, stop)
|
340
384
|
end
|
341
385
|
|
@@ -353,7 +397,7 @@ class Redis
|
|
353
397
|
get(key)
|
354
398
|
end
|
355
399
|
|
356
|
-
def []=(key,value)
|
400
|
+
def []=(key, value)
|
357
401
|
set(key, value)
|
358
402
|
end
|
359
403
|
|
@@ -362,6 +406,21 @@ class Redis
|
|
362
406
|
node_for(key).llen(key)
|
363
407
|
end
|
364
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
|
+
|
365
424
|
# Prepend one or more values to a list.
|
366
425
|
def lpush(key, value)
|
367
426
|
node_for(key).lpush(key, value)
|
@@ -382,14 +441,14 @@ class Redis
|
|
382
441
|
node_for(key).rpushx(key, value)
|
383
442
|
end
|
384
443
|
|
385
|
-
# Remove and get the first
|
386
|
-
def lpop(key)
|
387
|
-
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)
|
388
447
|
end
|
389
448
|
|
390
|
-
# Remove and get the last
|
391
|
-
def rpop(key)
|
392
|
-
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)
|
393
452
|
end
|
394
453
|
|
395
454
|
# Remove the last element in a list, append it to another list and return
|
@@ -401,13 +460,12 @@ class Redis
|
|
401
460
|
end
|
402
461
|
|
403
462
|
def _bpop(cmd, args)
|
404
|
-
|
405
|
-
|
406
|
-
if args.last.is_a?(Hash)
|
463
|
+
timeout = if args.last.is_a?(Hash)
|
407
464
|
options = args.pop
|
465
|
+
options[:timeout]
|
408
466
|
elsif args.last.respond_to?(:to_int)
|
409
467
|
# Issue deprecation notice in obnoxious mode...
|
410
|
-
|
468
|
+
args.pop.to_int
|
411
469
|
end
|
412
470
|
|
413
471
|
if args.size > 1
|
@@ -417,7 +475,11 @@ class Redis
|
|
417
475
|
keys = args.flatten
|
418
476
|
|
419
477
|
ensure_same_node(cmd, keys) do |node|
|
420
|
-
|
478
|
+
if timeout
|
479
|
+
node.__send__(cmd, keys, timeout: timeout)
|
480
|
+
else
|
481
|
+
node.__send__(cmd, keys)
|
482
|
+
end
|
421
483
|
end
|
422
484
|
end
|
423
485
|
|
@@ -435,15 +497,9 @@ class Redis
|
|
435
497
|
|
436
498
|
# Pop a value from a list, push it to another list and return it; or block
|
437
499
|
# until one is available.
|
438
|
-
def brpoplpush(source, destination,
|
439
|
-
case options
|
440
|
-
when Integer
|
441
|
-
# Issue deprecation notice in obnoxious mode...
|
442
|
-
options = { :timeout => options }
|
443
|
-
end
|
444
|
-
|
500
|
+
def brpoplpush(source, destination, deprecated_timeout = 0, **options)
|
445
501
|
ensure_same_node(:brpoplpush, [source, destination]) do |node|
|
446
|
-
node.brpoplpush(source, destination, options)
|
502
|
+
node.brpoplpush(source, destination, deprecated_timeout, **options)
|
447
503
|
end
|
448
504
|
end
|
449
505
|
|
@@ -514,19 +570,24 @@ class Redis
|
|
514
570
|
node_for(key).sismember(key, member)
|
515
571
|
end
|
516
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
|
+
|
517
578
|
# Get all the members in a set.
|
518
579
|
def smembers(key)
|
519
580
|
node_for(key).smembers(key)
|
520
581
|
end
|
521
582
|
|
522
583
|
# Scan a set
|
523
|
-
def sscan(key, cursor, options
|
524
|
-
node_for(key).sscan(key, cursor, options)
|
584
|
+
def sscan(key, cursor, **options)
|
585
|
+
node_for(key).sscan(key, cursor, **options)
|
525
586
|
end
|
526
587
|
|
527
588
|
# Scan a set and return an enumerator
|
528
|
-
def sscan_each(key, options
|
529
|
-
node_for(key).sscan_each(key, options, &block)
|
589
|
+
def sscan_each(key, **options, &block)
|
590
|
+
node_for(key).sscan_each(key, **options, &block)
|
530
591
|
end
|
531
592
|
|
532
593
|
# Subtract multiple sets.
|
@@ -581,6 +642,7 @@ class Redis
|
|
581
642
|
def zadd(key, *args)
|
582
643
|
node_for(key).zadd(key, *args)
|
583
644
|
end
|
645
|
+
ruby2_keywords(:zadd) if respond_to?(:ruby2_keywords, true)
|
584
646
|
|
585
647
|
# Increment the score of a member in a sorted set.
|
586
648
|
def zincrby(key, increment, member)
|
@@ -597,15 +659,33 @@ class Redis
|
|
597
659
|
node_for(key).zscore(key, member)
|
598
660
|
end
|
599
661
|
|
600
|
-
#
|
601
|
-
def
|
602
|
-
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
|
603
683
|
end
|
604
684
|
|
605
685
|
# Return a range of members in a sorted set, by index, with scores ordered
|
606
686
|
# from high to low.
|
607
|
-
def zrevrange(key, start, stop, options
|
608
|
-
node_for(key).zrevrange(key, start, stop, options)
|
687
|
+
def zrevrange(key, start, stop, **options)
|
688
|
+
node_for(key).zrevrange(key, start, stop, **options)
|
609
689
|
end
|
610
690
|
|
611
691
|
# Determine the index of a member in a sorted set.
|
@@ -625,14 +705,14 @@ class Redis
|
|
625
705
|
end
|
626
706
|
|
627
707
|
# Return a range of members in a sorted set, by score.
|
628
|
-
def zrangebyscore(key, min, max, options
|
629
|
-
node_for(key).zrangebyscore(key, min, max, options)
|
708
|
+
def zrangebyscore(key, min, max, **options)
|
709
|
+
node_for(key).zrangebyscore(key, min, max, **options)
|
630
710
|
end
|
631
711
|
|
632
712
|
# Return a range of members in a sorted set, by score, with scores ordered
|
633
713
|
# from high to low.
|
634
|
-
def zrevrangebyscore(key, max, min, options
|
635
|
-
node_for(key).zrevrangebyscore(key, max, min, options)
|
714
|
+
def zrevrangebyscore(key, max, min, **options)
|
715
|
+
node_for(key).zrevrangebyscore(key, max, min, **options)
|
636
716
|
end
|
637
717
|
|
638
718
|
# Remove all members in a sorted set within the given scores.
|
@@ -645,18 +725,47 @@ class Redis
|
|
645
725
|
node_for(key).zcount(key, min, max)
|
646
726
|
end
|
647
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
|
+
|
648
735
|
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
649
736
|
# key.
|
650
|
-
def zinterstore(destination, keys, options
|
737
|
+
def zinterstore(destination, keys, **options)
|
651
738
|
ensure_same_node(:zinterstore, [destination] + keys) do |node|
|
652
|
-
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)
|
653
747
|
end
|
654
748
|
end
|
655
749
|
|
656
750
|
# Add multiple sorted sets and store the resulting sorted set in a new key.
|
657
|
-
def zunionstore(destination, keys, options
|
751
|
+
def zunionstore(destination, keys, **options)
|
658
752
|
ensure_same_node(:zunionstore, [destination] + keys) do |node|
|
659
|
-
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)
|
660
769
|
end
|
661
770
|
end
|
662
771
|
|
@@ -665,9 +774,9 @@ class Redis
|
|
665
774
|
node_for(key).hlen(key)
|
666
775
|
end
|
667
776
|
|
668
|
-
# Set
|
669
|
-
def hset(key,
|
670
|
-
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)
|
671
780
|
end
|
672
781
|
|
673
782
|
# Set the value of a hash field, only if the field does not exist.
|
@@ -698,6 +807,10 @@ class Redis
|
|
698
807
|
Hash[*fields.zip(hmget(key, *fields)).flatten]
|
699
808
|
end
|
700
809
|
|
810
|
+
def hrandfield(key, count = nil, **options)
|
811
|
+
node_for(key).hrandfield(key, count, **options)
|
812
|
+
end
|
813
|
+
|
701
814
|
# Delete one or more hash fields.
|
702
815
|
def hdel(key, *fields)
|
703
816
|
node_for(key).hdel(key, *fields)
|
@@ -739,7 +852,7 @@ class Redis
|
|
739
852
|
end
|
740
853
|
|
741
854
|
def subscribed?
|
742
|
-
|
855
|
+
!!@subscribed_node
|
743
856
|
end
|
744
857
|
|
745
858
|
# Listen for messages published to the given channels.
|
@@ -757,7 +870,8 @@ class Redis
|
|
757
870
|
|
758
871
|
# Stop listening for messages posted to the given channels.
|
759
872
|
def unsubscribe(*channels)
|
760
|
-
raise
|
873
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
874
|
+
|
761
875
|
@subscribed_node.unsubscribe(*channels)
|
762
876
|
end
|
763
877
|
|
@@ -773,13 +887,26 @@ class Redis
|
|
773
887
|
end
|
774
888
|
|
775
889
|
# Watch the given keys to determine execution of the MULTI/EXEC block.
|
776
|
-
def watch(*keys)
|
777
|
-
|
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
|
778
901
|
end
|
779
902
|
|
780
903
|
# Forget about all watched keys.
|
781
904
|
def unwatch
|
782
|
-
raise CannotDistribute, :unwatch
|
905
|
+
raise CannotDistribute, :unwatch unless @watch_key
|
906
|
+
|
907
|
+
result = node_for(@watch_key).unwatch
|
908
|
+
@watch_key = nil
|
909
|
+
result
|
783
910
|
end
|
784
911
|
|
785
912
|
def pipelined
|
@@ -787,18 +914,30 @@ class Redis
|
|
787
914
|
end
|
788
915
|
|
789
916
|
# Mark the start of a transaction block.
|
790
|
-
def multi
|
791
|
-
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
|
792
923
|
end
|
793
924
|
|
794
925
|
# Execute all commands issued after MULTI.
|
795
926
|
def exec
|
796
|
-
raise CannotDistribute, :exec
|
927
|
+
raise CannotDistribute, :exec unless @watch_key
|
928
|
+
|
929
|
+
result = node_for(@watch_key).exec
|
930
|
+
@watch_key = nil
|
931
|
+
result
|
797
932
|
end
|
798
933
|
|
799
934
|
# Discard all commands issued after MULTI.
|
800
935
|
def discard
|
801
|
-
raise CannotDistribute, :discard
|
936
|
+
raise CannotDistribute, :discard unless @watch_key
|
937
|
+
|
938
|
+
result = node_for(@watch_key).discard
|
939
|
+
@watch_key = nil
|
940
|
+
result
|
802
941
|
end
|
803
942
|
|
804
943
|
# Control remote script registry.
|
@@ -857,7 +996,7 @@ class Redis
|
|
857
996
|
self.class.new(@node_configs, @default_options)
|
858
997
|
end
|
859
998
|
|
860
|
-
|
999
|
+
protected
|
861
1000
|
|
862
1001
|
def on_each_node(command, *args)
|
863
1002
|
nodes.map do |node|
|
data/lib/redis/errors.rb
CHANGED
data/lib/redis/hash_ring.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'zlib'
|
2
4
|
|
3
5
|
class Redis
|
4
6
|
class HashRing
|
5
|
-
|
6
7
|
POINTS_PER_SERVER = 160 # this is the default in libmemcached
|
7
8
|
|
8
9
|
attr_reader :ring, :sorted_keys, :replicas, :nodes
|
@@ -10,7 +11,7 @@ class Redis
|
|
10
11
|
# nodes is a list of objects that have a proper to_s representation.
|
11
12
|
# replicas indicates how many virtual points should be used pr. node,
|
12
13
|
# replicas are required to improve the distribution.
|
13
|
-
def initialize(nodes=[], replicas=POINTS_PER_SERVER)
|
14
|
+
def initialize(nodes = [], replicas = POINTS_PER_SERVER)
|
14
15
|
@replicas = replicas
|
15
16
|
@ring = {}
|
16
17
|
@nodes = []
|
@@ -32,11 +33,11 @@ class Redis
|
|
32
33
|
end
|
33
34
|
|
34
35
|
def remove_node(node)
|
35
|
-
@nodes.reject!{|n| n.id == node.id}
|
36
|
+
@nodes.reject! { |n| n.id == node.id }
|
36
37
|
@replicas.times do |i|
|
37
38
|
key = Zlib.crc32("#{node.id}:#{i}")
|
38
39
|
@ring.delete(key)
|
39
|
-
@sorted_keys.reject! {|k| k == key}
|
40
|
+
@sorted_keys.reject! { |k| k == key }
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
@@ -46,27 +47,29 @@ class Redis
|
|
46
47
|
end
|
47
48
|
|
48
49
|
def get_node_pos(key)
|
49
|
-
return [nil,nil] if @ring.
|
50
|
+
return [nil, nil] if @ring.empty?
|
51
|
+
|
50
52
|
crc = Zlib.crc32(key)
|
51
53
|
idx = HashRing.binary_search(@sorted_keys, crc)
|
52
|
-
|
54
|
+
[@ring[@sorted_keys[idx]], idx]
|
53
55
|
end
|
54
56
|
|
55
57
|
def iter_nodes(key)
|
56
|
-
return [nil,nil] if @ring.
|
58
|
+
return [nil, nil] if @ring.empty?
|
59
|
+
|
57
60
|
_, pos = get_node_pos(key)
|
58
61
|
@ring.size.times do |n|
|
59
|
-
yield @ring[@sorted_keys[(pos+n) % @ring.size]]
|
62
|
+
yield @ring[@sorted_keys[(pos + n) % @ring.size]]
|
60
63
|
end
|
61
64
|
end
|
62
65
|
|
63
66
|
# Find the closest index in HashRing with value <= the given value
|
64
|
-
def self.binary_search(ary, value
|
67
|
+
def self.binary_search(ary, value)
|
65
68
|
upper = ary.size - 1
|
66
69
|
lower = 0
|
67
70
|
idx = 0
|
68
71
|
|
69
|
-
while
|
72
|
+
while lower <= upper
|
70
73
|
idx = (lower + upper) / 2
|
71
74
|
comp = ary[idx] <=> value
|
72
75
|
|
@@ -79,10 +82,8 @@ class Redis
|
|
79
82
|
end
|
80
83
|
end
|
81
84
|
|
82
|
-
if upper < 0
|
83
|
-
|
84
|
-
end
|
85
|
-
return upper
|
85
|
+
upper = ary.size - 1 if upper < 0
|
86
|
+
upper
|
86
87
|
end
|
87
88
|
end
|
88
89
|
end
|