redis 4.2.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.
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "hash_ring"
3
+ require "redis/hash_ring"
4
4
 
5
5
  class Redis
6
6
  class Distributed
@@ -24,10 +24,14 @@ class Redis
24
24
  @default_options = options.dup
25
25
  node_configs.each { |node_config| add_node(node_config) }
26
26
  @subscribed_node = nil
27
+ @watch_key = nil
27
28
  end
28
29
 
29
30
  def node_for(key)
30
- @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)
31
35
  end
32
36
 
33
37
  def nodes
@@ -174,15 +178,11 @@ class Redis
174
178
  # Determine if a key exists.
175
179
  def exists(*args)
176
180
  if !Redis.exists_returns_integer && args.size == 1
177
- message = "`Redis#exists(key)` will return an Integer in redis-rb 4.3, if you want to keep the old behavior, " \
181
+ ::Redis.deprecate!(
182
+ "`Redis#exists(key)` will return an Integer in redis-rb 4.3, if you want to keep the old behavior, " \
178
183
  "use `exists?` instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = true. " \
179
184
  "(#{::Kernel.caller(1, 1).first})\n"
180
-
181
- if defined?(::Warning)
182
- ::Warning.warn(message)
183
- else
184
- warn(message)
185
- end
185
+ )
186
186
  exists?(*args)
187
187
  else
188
188
  keys_per_node = args.group_by { |key| node_for(key) }
@@ -211,6 +211,13 @@ class Redis
211
211
  node_for(key).move(key, db)
212
212
  end
213
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
+
214
221
  # Return a random key from the keyspace.
215
222
  def randomkey
216
223
  raise CannotDistribute, :randomkey
@@ -312,6 +319,16 @@ class Redis
312
319
  node_for(key).get(key)
313
320
  end
314
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
+
315
332
  # Get the values of all the given keys as an Array.
316
333
  def mget(*keys)
317
334
  mapped_mget(*keys).values_at(*keys)
@@ -389,6 +406,21 @@ class Redis
389
406
  node_for(key).llen(key)
390
407
  end
391
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
+
392
424
  # Prepend one or more values to a list.
393
425
  def lpush(key, value)
394
426
  node_for(key).lpush(key, value)
@@ -409,14 +441,14 @@ class Redis
409
441
  node_for(key).rpushx(key, value)
410
442
  end
411
443
 
412
- # Remove and get the first element in a list.
413
- def lpop(key)
414
- 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)
415
447
  end
416
448
 
417
- # Remove and get the last element in a list.
418
- def rpop(key)
419
- 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)
420
452
  end
421
453
 
422
454
  # Remove the last element in a list, append it to another list and return
@@ -538,6 +570,11 @@ class Redis
538
570
  node_for(key).sismember(key, member)
539
571
  end
540
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
+
541
578
  # Get all the members in a set.
542
579
  def smembers(key)
543
580
  node_for(key).smembers(key)
@@ -622,11 +659,29 @@ class Redis
622
659
  node_for(key).zscore(key, member)
623
660
  end
624
661
 
625
- # Return a range of members in a sorted set, by index.
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.
626
673
  def zrange(key, start, stop, **options)
627
674
  node_for(key).zrange(key, start, stop, **options)
628
675
  end
629
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
683
+ end
684
+
630
685
  # Return a range of members in a sorted set, by index, with scores ordered
631
686
  # from high to low.
632
687
  def zrevrange(key, start, stop, **options)
@@ -670,6 +725,13 @@ class Redis
670
725
  node_for(key).zcount(key, min, max)
671
726
  end
672
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
+
673
735
  # Intersect multiple sorted sets and store the resulting sorted set in a new
674
736
  # key.
675
737
  def zinterstore(destination, keys, **options)
@@ -678,6 +740,13 @@ class Redis
678
740
  end
679
741
  end
680
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)
747
+ end
748
+ end
749
+
681
750
  # Add multiple sorted sets and store the resulting sorted set in a new key.
682
751
  def zunionstore(destination, keys, **options)
683
752
  ensure_same_node(:zunionstore, [destination] + keys) do |node|
@@ -685,6 +754,21 @@ class Redis
685
754
  end
686
755
  end
687
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)
769
+ end
770
+ end
771
+
688
772
  # Get the number of fields in a hash.
689
773
  def hlen(key)
690
774
  node_for(key).hlen(key)
@@ -723,6 +807,10 @@ class Redis
723
807
  Hash[*fields.zip(hmget(key, *fields)).flatten]
724
808
  end
725
809
 
810
+ def hrandfield(key, count = nil, **options)
811
+ node_for(key).hrandfield(key, count, **options)
812
+ end
813
+
726
814
  # Delete one or more hash fields.
727
815
  def hdel(key, *fields)
728
816
  node_for(key).hdel(key, *fields)
@@ -799,13 +887,26 @@ class Redis
799
887
  end
800
888
 
801
889
  # Watch the given keys to determine execution of the MULTI/EXEC block.
802
- def watch(*_keys)
803
- raise CannotDistribute, :watch
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
804
901
  end
805
902
 
806
903
  # Forget about all watched keys.
807
904
  def unwatch
808
- raise CannotDistribute, :unwatch
905
+ raise CannotDistribute, :unwatch unless @watch_key
906
+
907
+ result = node_for(@watch_key).unwatch
908
+ @watch_key = nil
909
+ result
809
910
  end
810
911
 
811
912
  def pipelined
@@ -813,18 +914,30 @@ class Redis
813
914
  end
814
915
 
815
916
  # Mark the start of a transaction block.
816
- def multi
817
- 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
818
923
  end
819
924
 
820
925
  # Execute all commands issued after MULTI.
821
926
  def exec
822
- raise CannotDistribute, :exec
927
+ raise CannotDistribute, :exec unless @watch_key
928
+
929
+ result = node_for(@watch_key).exec
930
+ @watch_key = nil
931
+ result
823
932
  end
824
933
 
825
934
  # Discard all commands issued after MULTI.
826
935
  def discard
827
- raise CannotDistribute, :discard
936
+ raise CannotDistribute, :discard unless @watch_key
937
+
938
+ result = node_for(@watch_key).discard
939
+ @watch_key = nil
940
+ result
828
941
  end
829
942
 
830
943
  # Control remote script registry.
@@ -1,7 +1,70 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "delegate"
4
+
3
5
  class Redis
6
+ class PipelinedConnection
7
+ def initialize(pipeline)
8
+ @pipeline = pipeline
9
+ end
10
+
11
+ include Commands
12
+
13
+ def db
14
+ @pipeline.db
15
+ end
16
+
17
+ def db=(db)
18
+ @pipeline.db = db
19
+ end
20
+
21
+ def pipelined
22
+ yield self
23
+ end
24
+
25
+ private
26
+
27
+ def synchronize
28
+ yield self
29
+ end
30
+
31
+ def send_command(command, &block)
32
+ @pipeline.call(command, &block)
33
+ end
34
+
35
+ def send_blocking_command(command, timeout, &block)
36
+ @pipeline.call_with_timeout(command, timeout, &block)
37
+ end
38
+ end
39
+
4
40
  class Pipeline
41
+ REDIS_INTERNAL_PATH = File.expand_path("..", __dir__).freeze
42
+ # Redis use MonitorMixin#synchronize and this class use DelegateClass which we want to filter out.
43
+ # Both are in the stdlib so we can simply filter the entire stdlib out.
44
+ STDLIB_PATH = File.expand_path("..", MonitorMixin.instance_method(:synchronize).source_location.first).freeze
45
+
46
+ class << self
47
+ def deprecation_warning(method, caller_locations) # :nodoc:
48
+ callsite = caller_locations.find { |l| !l.path.start_with?(REDIS_INTERNAL_PATH, STDLIB_PATH) }
49
+ callsite ||= caller_locations.last # The caller_locations should be large enough, but just in case.
50
+ ::Redis.deprecate! <<~MESSAGE
51
+ Pipelining commands on a Redis instance is deprecated and will be removed in Redis 5.0.0.
52
+
53
+ redis.#{method} do
54
+ redis.get("key")
55
+ end
56
+
57
+ should be replaced by
58
+
59
+ redis.#{method} do |pipeline|
60
+ pipeline.get("key")
61
+ end
62
+
63
+ (called from #{callsite}}
64
+ MESSAGE
65
+ end
66
+ end
67
+
5
68
  attr_accessor :db
6
69
  attr_reader :client
7
70
 
@@ -124,6 +187,36 @@ class Redis
124
187
  end
125
188
  end
126
189
 
190
+ class DeprecatedPipeline < DelegateClass(Pipeline)
191
+ def initialize(pipeline)
192
+ super(pipeline)
193
+ @deprecation_displayed = false
194
+ end
195
+
196
+ def __getobj__
197
+ unless @deprecation_displayed
198
+ Pipeline.deprecation_warning("pipelined", Kernel.caller_locations(1, 10))
199
+ @deprecation_displayed = true
200
+ end
201
+ @delegate_dc_obj
202
+ end
203
+ end
204
+
205
+ class DeprecatedMulti < DelegateClass(Pipeline::Multi)
206
+ def initialize(pipeline)
207
+ super(pipeline)
208
+ @deprecation_displayed = false
209
+ end
210
+
211
+ def __getobj__
212
+ unless @deprecation_displayed
213
+ Pipeline.deprecation_warning("multi", Kernel.caller_locations(1, 10))
214
+ @deprecation_displayed = true
215
+ end
216
+ @delegate_dc_obj
217
+ end
218
+ end
219
+
127
220
  class FutureNotReady < RuntimeError
128
221
  def initialize
129
222
  super("Value will be available once the pipeline executes.")
@@ -143,11 +236,11 @@ class Redis
143
236
  end
144
237
 
145
238
  def ==(_other)
146
- message = +"The methods == and != are deprecated for Redis::Future and will be removed in 4.2.0"
239
+ message = +"The methods == and != are deprecated for Redis::Future and will be removed in 5.0.0"
147
240
  message << " - You probably meant to call .value == or .value !="
148
241
  message << " (#{::Kernel.caller(1, 1).first})\n"
149
242
 
150
- ::Kernel.warn(message)
243
+ ::Redis.deprecate!(message)
151
244
 
152
245
  super
153
246
  end
data/lib/redis/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Redis
4
- VERSION = '4.2.0'
4
+ VERSION = '4.6.0'
5
5
  end