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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +114 -0
- data/README.md +27 -21
- data/lib/redis/client.rb +52 -13
- data/lib/redis/cluster/command.rb +4 -6
- data/lib/redis/cluster/command_loader.rb +6 -7
- data/lib/redis/cluster/node.rb +14 -1
- data/lib/redis/cluster/option.rb +5 -2
- data/lib/redis/cluster.rb +33 -13
- 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 +2 -0
- data/lib/redis/connection/hiredis.rb +3 -2
- data/lib/redis/connection/ruby.rb +65 -56
- data/lib/redis/connection/synchrony.rb +10 -8
- data/lib/redis/connection.rb +1 -1
- data/lib/redis/distributed.rb +136 -23
- data/lib/redis/pipeline.rb +95 -2
- data/lib/redis/version.rb +1 -1
- data/lib/redis.rb +145 -3369
- metadata +30 -8
data/lib/redis/distributed.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
-
#
|
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(*
|
803
|
-
|
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.
|
data/lib/redis/pipeline.rb
CHANGED
@@ -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
|
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
|
-
::
|
243
|
+
::Redis.deprecate!(message)
|
151
244
|
|
152
245
|
super
|
153
246
|
end
|
data/lib/redis/version.rb
CHANGED