redis 4.1.4 → 4.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
 
@@ -286,6 +316,16 @@ class Redis
286
316
  node_for(key).get(key)
287
317
  end
288
318
 
319
+ # Get the value of a key and delete it.
320
+ def getdel(key)
321
+ node_for(key).getdel(key)
322
+ end
323
+
324
+ # Get the value of a key and sets its time to live based on options.
325
+ def getex(key, **options)
326
+ node_for(key).getex(key, **options)
327
+ end
328
+
289
329
  # Get the values of all the given keys as an Array.
290
330
  def mget(*keys)
291
331
  mapped_mget(*keys).values_at(*keys)
@@ -336,7 +376,7 @@ class Redis
336
376
  end
337
377
 
338
378
  # Return the position of the first bit set to 1 or 0 in a string.
339
- def bitpos(key, bit, start=nil, stop=nil)
379
+ def bitpos(key, bit, start = nil, stop = nil)
340
380
  node_for(key).bitpos(key, bit, start, stop)
341
381
  end
342
382
 
@@ -354,7 +394,7 @@ class Redis
354
394
  get(key)
355
395
  end
356
396
 
357
- def []=(key,value)
397
+ def []=(key, value)
358
398
  set(key, value)
359
399
  end
360
400
 
@@ -363,6 +403,21 @@ class Redis
363
403
  node_for(key).llen(key)
364
404
  end
365
405
 
406
+ # Remove the first/last element in a list, append/prepend it to another list and return it.
407
+ def lmove(source, destination, where_source, where_destination)
408
+ ensure_same_node(:lmove, [source, destination]) do |node|
409
+ node.lmove(source, destination, where_source, where_destination)
410
+ end
411
+ end
412
+
413
+ # Remove the first/last element in a list and append/prepend it
414
+ # to another list and return it, or block until one is available.
415
+ def blmove(source, destination, where_source, where_destination, timeout: 0)
416
+ ensure_same_node(:lmove, [source, destination]) do |node|
417
+ node.blmove(source, destination, where_source, where_destination, timeout: timeout)
418
+ end
419
+ end
420
+
366
421
  # Prepend one or more values to a list.
367
422
  def lpush(key, value)
368
423
  node_for(key).lpush(key, value)
@@ -383,14 +438,14 @@ class Redis
383
438
  node_for(key).rpushx(key, value)
384
439
  end
385
440
 
386
- # Remove and get the first element in a list.
387
- def lpop(key)
388
- node_for(key).lpop(key)
441
+ # Remove and get the first elements in a list.
442
+ def lpop(key, count = nil)
443
+ node_for(key).lpop(key, count)
389
444
  end
390
445
 
391
- # Remove and get the last element in a list.
392
- def rpop(key)
393
- node_for(key).rpop(key)
446
+ # Remove and get the last elements in a list.
447
+ def rpop(key, count = nil)
448
+ node_for(key).rpop(key, count)
394
449
  end
395
450
 
396
451
  # Remove the last element in a list, append it to another list and return
@@ -439,15 +494,9 @@ class Redis
439
494
 
440
495
  # Pop a value from a list, push it to another list and return it; or block
441
496
  # 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
-
497
+ def brpoplpush(source, destination, deprecated_timeout = 0, **options)
449
498
  ensure_same_node(:brpoplpush, [source, destination]) do |node|
450
- node.brpoplpush(source, destination, options)
499
+ node.brpoplpush(source, destination, deprecated_timeout, **options)
451
500
  end
452
501
  end
453
502
 
@@ -518,19 +567,24 @@ class Redis
518
567
  node_for(key).sismember(key, member)
519
568
  end
520
569
 
570
+ # Determine if multiple values are members of a set.
571
+ def smismember(key, *members)
572
+ node_for(key).smismember(key, *members)
573
+ end
574
+
521
575
  # Get all the members in a set.
522
576
  def smembers(key)
523
577
  node_for(key).smembers(key)
524
578
  end
525
579
 
526
580
  # Scan a set
527
- def sscan(key, cursor, options={})
528
- node_for(key).sscan(key, cursor, options)
581
+ def sscan(key, cursor, **options)
582
+ node_for(key).sscan(key, cursor, **options)
529
583
  end
530
584
 
531
585
  # Scan a set and return an enumerator
532
- def sscan_each(key, options={}, &block)
533
- node_for(key).sscan_each(key, options, &block)
586
+ def sscan_each(key, **options, &block)
587
+ node_for(key).sscan_each(key, **options, &block)
534
588
  end
535
589
 
536
590
  # Subtract multiple sets.
@@ -585,6 +639,7 @@ class Redis
585
639
  def zadd(key, *args)
586
640
  node_for(key).zadd(key, *args)
587
641
  end
642
+ ruby2_keywords(:zadd) if respond_to?(:ruby2_keywords, true)
588
643
 
589
644
  # Increment the score of a member in a sorted set.
590
645
  def zincrby(key, increment, member)
@@ -601,15 +656,25 @@ class Redis
601
656
  node_for(key).zscore(key, member)
602
657
  end
603
658
 
659
+ # Get one or more random members from a sorted set.
660
+ def zrandmember(key, count = nil, **options)
661
+ node_for(key).zrandmember(key, count, **options)
662
+ end
663
+
664
+ # Get the scores associated with the given members in a sorted set.
665
+ def zmscore(key, *members)
666
+ node_for(key).zmscore(key, *members)
667
+ end
668
+
604
669
  # 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)
670
+ def zrange(key, start, stop, **options)
671
+ node_for(key).zrange(key, start, stop, **options)
607
672
  end
608
673
 
609
674
  # Return a range of members in a sorted set, by index, with scores ordered
610
675
  # from high to low.
611
- def zrevrange(key, start, stop, options = {})
612
- node_for(key).zrevrange(key, start, stop, options)
676
+ def zrevrange(key, start, stop, **options)
677
+ node_for(key).zrevrange(key, start, stop, **options)
613
678
  end
614
679
 
615
680
  # Determine the index of a member in a sorted set.
@@ -629,14 +694,14 @@ class Redis
629
694
  end
630
695
 
631
696
  # 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)
697
+ def zrangebyscore(key, min, max, **options)
698
+ node_for(key).zrangebyscore(key, min, max, **options)
634
699
  end
635
700
 
636
701
  # Return a range of members in a sorted set, by score, with scores ordered
637
702
  # from high to low.
638
- def zrevrangebyscore(key, max, min, options = {})
639
- node_for(key).zrevrangebyscore(key, max, min, options)
703
+ def zrevrangebyscore(key, max, min, **options)
704
+ node_for(key).zrevrangebyscore(key, max, min, **options)
640
705
  end
641
706
 
642
707
  # Remove all members in a sorted set within the given scores.
@@ -649,18 +714,25 @@ class Redis
649
714
  node_for(key).zcount(key, min, max)
650
715
  end
651
716
 
717
+ # Get the intersection of multiple sorted sets
718
+ def zinter(*keys, **options)
719
+ ensure_same_node(:zinter, keys) do |node|
720
+ node.zinter(*keys, **options)
721
+ end
722
+ end
723
+
652
724
  # Intersect multiple sorted sets and store the resulting sorted set in a new
653
725
  # key.
654
- def zinterstore(destination, keys, options = {})
726
+ def zinterstore(destination, keys, **options)
655
727
  ensure_same_node(:zinterstore, [destination] + keys) do |node|
656
- node.zinterstore(destination, keys, options)
728
+ node.zinterstore(destination, keys, **options)
657
729
  end
658
730
  end
659
731
 
660
732
  # Add multiple sorted sets and store the resulting sorted set in a new key.
661
- def zunionstore(destination, keys, options = {})
733
+ def zunionstore(destination, keys, **options)
662
734
  ensure_same_node(:zunionstore, [destination] + keys) do |node|
663
- node.zunionstore(destination, keys, options)
735
+ node.zunionstore(destination, keys, **options)
664
736
  end
665
737
  end
666
738
 
@@ -669,9 +741,9 @@ class Redis
669
741
  node_for(key).hlen(key)
670
742
  end
671
743
 
672
- # Set the string value of a hash field.
673
- def hset(key, field, value)
674
- node_for(key).hset(key, field, value)
744
+ # Set multiple hash fields to multiple values.
745
+ def hset(key, *attrs)
746
+ node_for(key).hset(key, *attrs)
675
747
  end
676
748
 
677
749
  # Set the value of a hash field, only if the field does not exist.
@@ -743,7 +815,7 @@ class Redis
743
815
  end
744
816
 
745
817
  def subscribed?
746
- !! @subscribed_node
818
+ !!@subscribed_node
747
819
  end
748
820
 
749
821
  # Listen for messages published to the given channels.
@@ -761,7 +833,8 @@ class Redis
761
833
 
762
834
  # Stop listening for messages posted to the given channels.
763
835
  def unsubscribe(*channels)
764
- raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
836
+ raise "Can't unsubscribe if not subscribed." unless subscribed?
837
+
765
838
  @subscribed_node.unsubscribe(*channels)
766
839
  end
767
840
 
@@ -777,13 +850,26 @@ class Redis
777
850
  end
778
851
 
779
852
  # Watch the given keys to determine execution of the MULTI/EXEC block.
780
- def watch(*keys)
781
- raise CannotDistribute, :watch
853
+ def watch(*keys, &block)
854
+ ensure_same_node(:watch, keys) do |node|
855
+ @watch_key = key_tag(keys.first) || keys.first.to_s
856
+
857
+ begin
858
+ node.watch(*keys, &block)
859
+ rescue StandardError
860
+ @watch_key = nil
861
+ raise
862
+ end
863
+ end
782
864
  end
783
865
 
784
866
  # Forget about all watched keys.
785
867
  def unwatch
786
- raise CannotDistribute, :unwatch
868
+ raise CannotDistribute, :unwatch unless @watch_key
869
+
870
+ result = node_for(@watch_key).unwatch
871
+ @watch_key = nil
872
+ result
787
873
  end
788
874
 
789
875
  def pipelined
@@ -791,18 +877,30 @@ class Redis
791
877
  end
792
878
 
793
879
  # Mark the start of a transaction block.
794
- def multi
795
- raise CannotDistribute, :multi
880
+ def multi(&block)
881
+ raise CannotDistribute, :multi unless @watch_key
882
+
883
+ result = node_for(@watch_key).multi(&block)
884
+ @watch_key = nil if block_given?
885
+ result
796
886
  end
797
887
 
798
888
  # Execute all commands issued after MULTI.
799
889
  def exec
800
- raise CannotDistribute, :exec
890
+ raise CannotDistribute, :exec unless @watch_key
891
+
892
+ result = node_for(@watch_key).exec
893
+ @watch_key = nil
894
+ result
801
895
  end
802
896
 
803
897
  # Discard all commands issued after MULTI.
804
898
  def discard
805
- raise CannotDistribute, :discard
899
+ raise CannotDistribute, :discard unless @watch_key
900
+
901
+ result = node_for(@watch_key).discard
902
+ @watch_key = nil
903
+ result
806
904
  end
807
905
 
808
906
  # Control remote script registry.
@@ -861,7 +959,7 @@ class Redis
861
959
  self.class.new(@node_configs, @default_options)
862
960
  end
863
961
 
864
- protected
962
+ protected
865
963
 
866
964
  def on_each_node(command, *args)
867
965
  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
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class Redis
3
4
  class Pipeline
4
5
  attr_accessor :db
@@ -61,7 +62,7 @@ class Redis
61
62
  @futures.map(&:timeout)
62
63
  end
63
64
 
64
- def with_reconnect(val=true)
65
+ def with_reconnect(val = true)
65
66
  @with_reconnect = false unless val
66
67
  yield
67
68
  end
@@ -93,7 +94,8 @@ class Redis
93
94
 
94
95
  if exec.size < futures.size
95
96
  # Some command wasn't recognized by Redis.
96
- raise replies.detect { |r| r.is_a?(CommandError) }
97
+ command_error = replies.detect { |r| r.is_a?(CommandError) }
98
+ raise command_error
97
99
  end
98
100
 
99
101
  super(exec) do |reply|
@@ -145,11 +147,7 @@ class Redis
145
147
  message << " - You probably meant to call .value == or .value !="
146
148
  message << " (#{::Kernel.caller(1, 1).first})\n"
147
149
 
148
- if defined?(::Warning)
149
- ::Warning.warn(message)
150
- else
151
- $stderr.puts(message)
152
- end
150
+ ::Kernel.warn(message)
153
151
 
154
152
  super
155
153
  end
@@ -168,7 +166,7 @@ class Redis
168
166
  end
169
167
 
170
168
  def value
171
- ::Kernel.raise(@object) if @object.kind_of?(::RuntimeError)
169
+ ::Kernel.raise(@object) if @object.is_a?(::RuntimeError)
172
170
  @object
173
171
  end
174
172
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class Redis
3
4
  class SubscribedClient
4
5
  def initialize(client)
@@ -33,24 +34,21 @@ class Redis
33
34
  call([:punsubscribe, *channels])
34
35
  end
35
36
 
36
- protected
37
+ protected
37
38
 
38
39
  def subscription(start, stop, channels, block, timeout = 0)
39
40
  sub = Subscription.new(&block)
40
41
 
41
42
  unsubscribed = false
42
43
 
43
- begin
44
- @client.call_loop([start, *channels], timeout) do |line|
45
- type, *rest = line
46
- sub.callbacks[type].call(*rest)
47
- unsubscribed = type == stop && rest.last == 0
48
- break if unsubscribed
49
- end
50
- ensure
51
- # No need to unsubscribe here. The real client closes the connection
52
- # whenever an exception is raised (see #ensure_connected).
44
+ @client.call_loop([start, *channels], timeout) do |line|
45
+ type, *rest = line
46
+ sub.callbacks[type].call(*rest)
47
+ unsubscribed = type == stop && rest.last == 0
48
+ break if unsubscribed
53
49
  end
50
+ # No need to unsubscribe here. The real client closes the connection
51
+ # whenever an exception is raised (see #ensure_connected).
54
52
  end
55
53
  end
56
54
 
@@ -59,7 +57,7 @@ class Redis
59
57
 
60
58
  def initialize
61
59
  @callbacks = Hash.new do |hash, key|
62
- hash[key] = lambda { |*_| }
60
+ hash[key] = ->(*_) {}
63
61
  end
64
62
 
65
63
  yield(self)
data/lib/redis/version.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class Redis
3
- VERSION = '4.1.4'
4
+ VERSION = '4.5.1'
4
5
  end