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/pipeline.rb
CHANGED
@@ -1,15 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "delegate"
|
4
|
+
|
1
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
|
+
|
2
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
|
+
|
3
68
|
attr_accessor :db
|
69
|
+
attr_reader :client
|
4
70
|
|
5
71
|
attr :futures
|
6
72
|
|
7
|
-
def initialize
|
73
|
+
def initialize(client)
|
74
|
+
@client = client.is_a?(Pipeline) ? client.client : client
|
8
75
|
@with_reconnect = true
|
9
76
|
@shutdown = false
|
10
77
|
@futures = []
|
11
78
|
end
|
12
79
|
|
80
|
+
def timeout
|
81
|
+
client.timeout
|
82
|
+
end
|
83
|
+
|
13
84
|
def with_reconnect?
|
14
85
|
@with_reconnect
|
15
86
|
end
|
@@ -26,15 +97,19 @@ class Redis
|
|
26
97
|
@futures.empty?
|
27
98
|
end
|
28
99
|
|
29
|
-
def call(command, &block)
|
100
|
+
def call(command, timeout: nil, &block)
|
30
101
|
# A pipeline that contains a shutdown should not raise ECONNRESET when
|
31
102
|
# the connection is gone.
|
32
103
|
@shutdown = true if command.first == :shutdown
|
33
|
-
future = Future.new(command, block)
|
104
|
+
future = Future.new(command, block, timeout)
|
34
105
|
@futures << future
|
35
106
|
future
|
36
107
|
end
|
37
108
|
|
109
|
+
def call_with_timeout(command, timeout, &block)
|
110
|
+
call(command, timeout: timeout, &block)
|
111
|
+
end
|
112
|
+
|
38
113
|
def call_pipeline(pipeline)
|
39
114
|
@shutdown = true if pipeline.shutdown?
|
40
115
|
@futures.concat(pipeline.futures)
|
@@ -43,10 +118,14 @@ class Redis
|
|
43
118
|
end
|
44
119
|
|
45
120
|
def commands
|
46
|
-
@futures.map
|
121
|
+
@futures.map(&:_command)
|
47
122
|
end
|
48
123
|
|
49
|
-
def
|
124
|
+
def timeouts
|
125
|
+
@futures.map(&:timeout)
|
126
|
+
end
|
127
|
+
|
128
|
+
def with_reconnect(val = true)
|
50
129
|
@with_reconnect = false unless val
|
51
130
|
yield
|
52
131
|
end
|
@@ -78,7 +157,8 @@ class Redis
|
|
78
157
|
|
79
158
|
if exec.size < futures.size
|
80
159
|
# Some command wasn't recognized by Redis.
|
81
|
-
|
160
|
+
command_error = replies.detect { |r| r.is_a?(CommandError) }
|
161
|
+
raise command_error
|
82
162
|
end
|
83
163
|
|
84
164
|
super(exec) do |reply|
|
@@ -89,6 +169,14 @@ class Redis
|
|
89
169
|
end
|
90
170
|
end
|
91
171
|
|
172
|
+
def timeouts
|
173
|
+
if empty?
|
174
|
+
[]
|
175
|
+
else
|
176
|
+
[nil, *super, nil]
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
92
180
|
def commands
|
93
181
|
if empty?
|
94
182
|
[]
|
@@ -99,6 +187,36 @@ class Redis
|
|
99
187
|
end
|
100
188
|
end
|
101
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
|
+
|
102
220
|
class FutureNotReady < RuntimeError
|
103
221
|
def initialize
|
104
222
|
super("Value will be available once the pipeline executes.")
|
@@ -108,12 +226,25 @@ class Redis
|
|
108
226
|
class Future < BasicObject
|
109
227
|
FutureNotReady = ::Redis::FutureNotReady.new
|
110
228
|
|
111
|
-
|
229
|
+
attr_reader :timeout
|
230
|
+
|
231
|
+
def initialize(command, transformation, timeout)
|
112
232
|
@command = command
|
113
233
|
@transformation = transformation
|
234
|
+
@timeout = timeout
|
114
235
|
@object = FutureNotReady
|
115
236
|
end
|
116
237
|
|
238
|
+
def ==(_other)
|
239
|
+
message = +"The methods == and != are deprecated for Redis::Future and will be removed in 5.0.0"
|
240
|
+
message << " - You probably meant to call .value == or .value !="
|
241
|
+
message << " (#{::Kernel.caller(1, 1).first})\n"
|
242
|
+
|
243
|
+
::Redis.deprecate!(message)
|
244
|
+
|
245
|
+
super
|
246
|
+
end
|
247
|
+
|
117
248
|
def inspect
|
118
249
|
"<Redis::Future #{@command.inspect}>"
|
119
250
|
end
|
@@ -128,7 +259,7 @@ class Redis
|
|
128
259
|
end
|
129
260
|
|
130
261
|
def value
|
131
|
-
::Kernel.raise(@object) if @object.
|
262
|
+
::Kernel.raise(@object) if @object.is_a?(::RuntimeError)
|
132
263
|
@object
|
133
264
|
end
|
134
265
|
|
data/lib/redis/subscribe.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class Redis
|
2
4
|
class SubscribedClient
|
3
5
|
def initialize(client)
|
@@ -32,24 +34,21 @@ class Redis
|
|
32
34
|
call([:punsubscribe, *channels])
|
33
35
|
end
|
34
36
|
|
35
|
-
|
37
|
+
protected
|
36
38
|
|
37
39
|
def subscription(start, stop, channels, block, timeout = 0)
|
38
40
|
sub = Subscription.new(&block)
|
39
41
|
|
40
42
|
unsubscribed = false
|
41
43
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
break if unsubscribed
|
48
|
-
end
|
49
|
-
ensure
|
50
|
-
# No need to unsubscribe here. The real client closes the connection
|
51
|
-
# 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
|
52
49
|
end
|
50
|
+
# No need to unsubscribe here. The real client closes the connection
|
51
|
+
# whenever an exception is raised (see #ensure_connected).
|
53
52
|
end
|
54
53
|
end
|
55
54
|
|
@@ -58,7 +57,7 @@ class Redis
|
|
58
57
|
|
59
58
|
def initialize
|
60
59
|
@callbacks = Hash.new do |hash, key|
|
61
|
-
hash[key] =
|
60
|
+
hash[key] = ->(*_) {}
|
62
61
|
end
|
63
62
|
|
64
63
|
yield(self)
|
data/lib/redis/version.rb
CHANGED