redis 4.1.4 → 4.4.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.
@@ -1,10 +1,16 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative "command_helper"
3
4
  require_relative "registry"
4
5
  require_relative "../errors"
5
6
  require "em-synchrony"
6
7
  require "hiredis/reader"
7
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
+
8
14
  class Redis
9
15
  module Connection
10
16
  class RedisClient < EventMachine::Connection
@@ -47,9 +53,7 @@ class Redis
47
53
 
48
54
  def read
49
55
  @req = EventMachine::DefaultDeferrable.new
50
- if @timeout > 0
51
- @req.timeout(@timeout, :timeout)
52
- end
56
+ @req.timeout(@timeout, :timeout) if @timeout > 0
53
57
  EventMachine::Synchrony.sync @req
54
58
  end
55
59
 
@@ -106,7 +110,7 @@ class Redis
106
110
  end
107
111
 
108
112
  def connected?
109
- @connection && @connection.connected?
113
+ @connection&.connected?
110
114
  end
111
115
 
112
116
  def timeout=(timeout)
@@ -1,16 +1,17 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative "hash_ring"
3
4
 
4
5
  class Redis
5
6
  class Distributed
6
-
7
7
  class CannotDistribute < RuntimeError
8
8
  def initialize(command)
9
9
  @command = command
10
10
  end
11
11
 
12
12
  def message
13
- "#{@command.to_s.upcase} cannot be used in Redis::Distributed because the keys involved need to be on the same server or because we cannot guarantee that the operation will be atomic."
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."
14
15
  end
15
16
  end
16
17
 
@@ -23,10 +24,14 @@ class Redis
23
24
  @default_options = options.dup
24
25
  node_configs.each { |node_config| add_node(node_config) }
25
26
  @subscribed_node = nil
27
+ @watch_key = nil
26
28
  end
27
29
 
28
30
  def node_for(key)
29
- @ring.get_node(key_tag(key.to_s) || key.to_s)
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)
30
35
  end
31
36
 
32
37
  def nodes
@@ -34,9 +39,9 @@ class Redis
34
39
  end
35
40
 
36
41
  def add_node(options)
37
- options = { :url => options } if options.is_a?(String)
42
+ options = { url: options } if options.is_a?(String)
38
43
  options = @default_options.merge(options)
39
- @ring.add_node Redis.new( options )
44
+ @ring.add_node Redis.new(options)
40
45
  end
41
46
 
42
47
  # Change the selected database for the current connection.
@@ -145,12 +150,12 @@ class Redis
145
150
  end
146
151
 
147
152
  # Create a key using the serialized value, previously obtained using DUMP.
148
- def restore(key, ttl, serialized_value, options = {})
149
- 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)
150
155
  end
151
156
 
152
157
  # Transfer a key from the connected instance to another instance.
153
- def migrate(key, options)
158
+ def migrate(_key, _options)
154
159
  raise CannotDistribute, :migrate
155
160
  end
156
161
 
@@ -171,8 +176,33 @@ class Redis
171
176
  end
172
177
 
173
178
  # Determine if a key exists.
174
- def exists(key)
175
- node_for(key).exists(key)
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
176
206
  end
177
207
 
178
208
  # Find all keys matching the given pattern.
@@ -205,11 +235,11 @@ class Redis
205
235
  end
206
236
 
207
237
  # Sort the elements in a list, set or sorted set.
208
- def sort(key, options = {})
238
+ def sort(key, **options)
209
239
  keys = [key, options[:by], options[:store], *Array(options[:get])].compact
210
240
 
211
241
  ensure_same_node(:sort, keys) do |node|
212
- node.sort(key, options)
242
+ node.sort(key, **options)
213
243
  end
214
244
  end
215
245
 
@@ -244,8 +274,8 @@ class Redis
244
274
  end
245
275
 
246
276
  # Set the string value of a key.
247
- def set(key, value, options = {})
248
- node_for(key).set(key, value, options)
277
+ def set(key, value, **options)
278
+ node_for(key).set(key, value, **options)
249
279
  end
250
280
 
251
281
  # Set the time to live in seconds of a key.
@@ -264,20 +294,20 @@ class Redis
264
294
  end
265
295
 
266
296
  # Set multiple keys to multiple values.
267
- def mset(*args)
297
+ def mset(*_args)
268
298
  raise CannotDistribute, :mset
269
299
  end
270
300
 
271
- def mapped_mset(hash)
301
+ def mapped_mset(_hash)
272
302
  raise CannotDistribute, :mapped_mset
273
303
  end
274
304
 
275
305
  # Set multiple keys to multiple values, only if none of the keys exist.
276
- def msetnx(*args)
306
+ def msetnx(*_args)
277
307
  raise CannotDistribute, :msetnx
278
308
  end
279
309
 
280
- def mapped_msetnx(hash)
310
+ def mapped_msetnx(_hash)
281
311
  raise CannotDistribute, :mapped_msetnx
282
312
  end
283
313
 
@@ -336,7 +366,7 @@ class Redis
336
366
  end
337
367
 
338
368
  # Return the position of the first bit set to 1 or 0 in a string.
339
- def bitpos(key, bit, start=nil, stop=nil)
369
+ def bitpos(key, bit, start = nil, stop = nil)
340
370
  node_for(key).bitpos(key, bit, start, stop)
341
371
  end
342
372
 
@@ -354,7 +384,7 @@ class Redis
354
384
  get(key)
355
385
  end
356
386
 
357
- def []=(key,value)
387
+ def []=(key, value)
358
388
  set(key, value)
359
389
  end
360
390
 
@@ -383,14 +413,14 @@ class Redis
383
413
  node_for(key).rpushx(key, value)
384
414
  end
385
415
 
386
- # Remove and get the first element in a list.
387
- def lpop(key)
388
- node_for(key).lpop(key)
416
+ # Remove and get the first elements in a list.
417
+ def lpop(key, count = nil)
418
+ node_for(key).lpop(key, count)
389
419
  end
390
420
 
391
- # Remove and get the last element in a list.
392
- def rpop(key)
393
- node_for(key).rpop(key)
421
+ # Remove and get the last elements in a list.
422
+ def rpop(key, count = nil)
423
+ node_for(key).rpop(key, count)
394
424
  end
395
425
 
396
426
  # Remove the last element in a list, append it to another list and return
@@ -439,15 +469,9 @@ class Redis
439
469
 
440
470
  # Pop a value from a list, push it to another list and return it; or block
441
471
  # until one is available.
442
- def brpoplpush(source, destination, options = {})
443
- case options
444
- when Integer
445
- # Issue deprecation notice in obnoxious mode...
446
- options = { :timeout => options }
447
- end
448
-
472
+ def brpoplpush(source, destination, deprecated_timeout = 0, **options)
449
473
  ensure_same_node(:brpoplpush, [source, destination]) do |node|
450
- node.brpoplpush(source, destination, options)
474
+ node.brpoplpush(source, destination, deprecated_timeout, **options)
451
475
  end
452
476
  end
453
477
 
@@ -524,13 +548,13 @@ class Redis
524
548
  end
525
549
 
526
550
  # Scan a set
527
- def sscan(key, cursor, options={})
528
- node_for(key).sscan(key, cursor, options)
551
+ def sscan(key, cursor, **options)
552
+ node_for(key).sscan(key, cursor, **options)
529
553
  end
530
554
 
531
555
  # Scan a set and return an enumerator
532
- def sscan_each(key, options={}, &block)
533
- node_for(key).sscan_each(key, options, &block)
556
+ def sscan_each(key, **options, &block)
557
+ node_for(key).sscan_each(key, **options, &block)
534
558
  end
535
559
 
536
560
  # Subtract multiple sets.
@@ -585,6 +609,7 @@ class Redis
585
609
  def zadd(key, *args)
586
610
  node_for(key).zadd(key, *args)
587
611
  end
612
+ ruby2_keywords(:zadd) if respond_to?(:ruby2_keywords, true)
588
613
 
589
614
  # Increment the score of a member in a sorted set.
590
615
  def zincrby(key, increment, member)
@@ -602,14 +627,14 @@ class Redis
602
627
  end
603
628
 
604
629
  # Return a range of members in a sorted set, by index.
605
- def zrange(key, start, stop, options = {})
606
- node_for(key).zrange(key, start, stop, options)
630
+ def zrange(key, start, stop, **options)
631
+ node_for(key).zrange(key, start, stop, **options)
607
632
  end
608
633
 
609
634
  # Return a range of members in a sorted set, by index, with scores ordered
610
635
  # from high to low.
611
- def zrevrange(key, start, stop, options = {})
612
- node_for(key).zrevrange(key, start, stop, options)
636
+ def zrevrange(key, start, stop, **options)
637
+ node_for(key).zrevrange(key, start, stop, **options)
613
638
  end
614
639
 
615
640
  # Determine the index of a member in a sorted set.
@@ -629,14 +654,14 @@ class Redis
629
654
  end
630
655
 
631
656
  # Return a range of members in a sorted set, by score.
632
- def zrangebyscore(key, min, max, options = {})
633
- node_for(key).zrangebyscore(key, min, max, options)
657
+ def zrangebyscore(key, min, max, **options)
658
+ node_for(key).zrangebyscore(key, min, max, **options)
634
659
  end
635
660
 
636
661
  # Return a range of members in a sorted set, by score, with scores ordered
637
662
  # from high to low.
638
- def zrevrangebyscore(key, max, min, options = {})
639
- node_for(key).zrevrangebyscore(key, max, min, options)
663
+ def zrevrangebyscore(key, max, min, **options)
664
+ node_for(key).zrevrangebyscore(key, max, min, **options)
640
665
  end
641
666
 
642
667
  # Remove all members in a sorted set within the given scores.
@@ -649,18 +674,25 @@ class Redis
649
674
  node_for(key).zcount(key, min, max)
650
675
  end
651
676
 
677
+ # Get the intersection of multiple sorted sets
678
+ def zinter(*keys, **options)
679
+ ensure_same_node(:zinter, keys) do |node|
680
+ node.zinter(*keys, **options)
681
+ end
682
+ end
683
+
652
684
  # Intersect multiple sorted sets and store the resulting sorted set in a new
653
685
  # key.
654
- def zinterstore(destination, keys, options = {})
686
+ def zinterstore(destination, keys, **options)
655
687
  ensure_same_node(:zinterstore, [destination] + keys) do |node|
656
- node.zinterstore(destination, keys, options)
688
+ node.zinterstore(destination, keys, **options)
657
689
  end
658
690
  end
659
691
 
660
692
  # Add multiple sorted sets and store the resulting sorted set in a new key.
661
- def zunionstore(destination, keys, options = {})
693
+ def zunionstore(destination, keys, **options)
662
694
  ensure_same_node(:zunionstore, [destination] + keys) do |node|
663
- node.zunionstore(destination, keys, options)
695
+ node.zunionstore(destination, keys, **options)
664
696
  end
665
697
  end
666
698
 
@@ -669,9 +701,9 @@ class Redis
669
701
  node_for(key).hlen(key)
670
702
  end
671
703
 
672
- # Set the string value of a hash field.
673
- def hset(key, field, value)
674
- node_for(key).hset(key, field, value)
704
+ # Set multiple hash fields to multiple values.
705
+ def hset(key, *attrs)
706
+ node_for(key).hset(key, *attrs)
675
707
  end
676
708
 
677
709
  # Set the value of a hash field, only if the field does not exist.
@@ -743,7 +775,7 @@ class Redis
743
775
  end
744
776
 
745
777
  def subscribed?
746
- !! @subscribed_node
778
+ !!@subscribed_node
747
779
  end
748
780
 
749
781
  # Listen for messages published to the given channels.
@@ -761,7 +793,8 @@ class Redis
761
793
 
762
794
  # Stop listening for messages posted to the given channels.
763
795
  def unsubscribe(*channels)
764
- raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
796
+ raise "Can't unsubscribe if not subscribed." unless subscribed?
797
+
765
798
  @subscribed_node.unsubscribe(*channels)
766
799
  end
767
800
 
@@ -777,13 +810,26 @@ class Redis
777
810
  end
778
811
 
779
812
  # Watch the given keys to determine execution of the MULTI/EXEC block.
780
- def watch(*keys)
781
- raise CannotDistribute, :watch
813
+ def watch(*keys, &block)
814
+ ensure_same_node(:watch, keys) do |node|
815
+ @watch_key = key_tag(keys.first) || keys.first.to_s
816
+
817
+ begin
818
+ node.watch(*keys, &block)
819
+ rescue StandardError
820
+ @watch_key = nil
821
+ raise
822
+ end
823
+ end
782
824
  end
783
825
 
784
826
  # Forget about all watched keys.
785
827
  def unwatch
786
- raise CannotDistribute, :unwatch
828
+ raise CannotDistribute, :unwatch unless @watch_key
829
+
830
+ result = node_for(@watch_key).unwatch
831
+ @watch_key = nil
832
+ result
787
833
  end
788
834
 
789
835
  def pipelined
@@ -791,18 +837,30 @@ class Redis
791
837
  end
792
838
 
793
839
  # Mark the start of a transaction block.
794
- def multi
795
- raise CannotDistribute, :multi
840
+ def multi(&block)
841
+ raise CannotDistribute, :multi unless @watch_key
842
+
843
+ result = node_for(@watch_key).multi(&block)
844
+ @watch_key = nil if block_given?
845
+ result
796
846
  end
797
847
 
798
848
  # Execute all commands issued after MULTI.
799
849
  def exec
800
- raise CannotDistribute, :exec
850
+ raise CannotDistribute, :exec unless @watch_key
851
+
852
+ result = node_for(@watch_key).exec
853
+ @watch_key = nil
854
+ result
801
855
  end
802
856
 
803
857
  # Discard all commands issued after MULTI.
804
858
  def discard
805
- raise CannotDistribute, :discard
859
+ raise CannotDistribute, :discard unless @watch_key
860
+
861
+ result = node_for(@watch_key).discard
862
+ @watch_key = nil
863
+ result
806
864
  end
807
865
 
808
866
  # Control remote script registry.
@@ -861,7 +919,7 @@ class Redis
861
919
  self.class.new(@node_configs, @default_options)
862
920
  end
863
921
 
864
- protected
922
+ protected
865
923
 
866
924
  def on_each_node(command, *args)
867
925
  nodes.map do |node|
data/lib/redis/errors.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class Redis
3
4
  # Base error for all redis-rb errors.
4
5
  class BaseError < RuntimeError
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'zlib'
3
4
 
4
5
  class Redis
5
6
  class HashRing
6
-
7
7
  POINTS_PER_SERVER = 160 # this is the default in libmemcached
8
8
 
9
9
  attr_reader :ring, :sorted_keys, :replicas, :nodes
@@ -11,7 +11,7 @@ class Redis
11
11
  # nodes is a list of objects that have a proper to_s representation.
12
12
  # replicas indicates how many virtual points should be used pr. node,
13
13
  # replicas are required to improve the distribution.
14
- def initialize(nodes=[], replicas=POINTS_PER_SERVER)
14
+ def initialize(nodes = [], replicas = POINTS_PER_SERVER)
15
15
  @replicas = replicas
16
16
  @ring = {}
17
17
  @nodes = []
@@ -33,11 +33,11 @@ class Redis
33
33
  end
34
34
 
35
35
  def remove_node(node)
36
- @nodes.reject!{|n| n.id == node.id}
36
+ @nodes.reject! { |n| n.id == node.id }
37
37
  @replicas.times do |i|
38
38
  key = Zlib.crc32("#{node.id}:#{i}")
39
39
  @ring.delete(key)
40
- @sorted_keys.reject! {|k| k == key}
40
+ @sorted_keys.reject! { |k| k == key }
41
41
  end
42
42
  end
43
43
 
@@ -47,27 +47,29 @@ class Redis
47
47
  end
48
48
 
49
49
  def get_node_pos(key)
50
- return [nil,nil] if @ring.size == 0
50
+ return [nil, nil] if @ring.empty?
51
+
51
52
  crc = Zlib.crc32(key)
52
53
  idx = HashRing.binary_search(@sorted_keys, crc)
53
- return [@ring[@sorted_keys[idx]], idx]
54
+ [@ring[@sorted_keys[idx]], idx]
54
55
  end
55
56
 
56
57
  def iter_nodes(key)
57
- return [nil,nil] if @ring.size == 0
58
+ return [nil, nil] if @ring.empty?
59
+
58
60
  _, pos = get_node_pos(key)
59
61
  @ring.size.times do |n|
60
- yield @ring[@sorted_keys[(pos+n) % @ring.size]]
62
+ yield @ring[@sorted_keys[(pos + n) % @ring.size]]
61
63
  end
62
64
  end
63
65
 
64
66
  # Find the closest index in HashRing with value <= the given value
65
- def self.binary_search(ary, value, &block)
67
+ def self.binary_search(ary, value)
66
68
  upper = ary.size - 1
67
69
  lower = 0
68
70
  idx = 0
69
71
 
70
- while(lower <= upper) do
72
+ while lower <= upper
71
73
  idx = (lower + upper) / 2
72
74
  comp = ary[idx] <=> value
73
75
 
@@ -80,10 +82,8 @@ class Redis
80
82
  end
81
83
  end
82
84
 
83
- if upper < 0
84
- upper = ary.size - 1
85
- end
86
- return upper
85
+ upper = ary.size - 1 if upper < 0
86
+ upper
87
87
  end
88
88
  end
89
89
  end