redis 4.7.1 → 5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +93 -0
- data/README.md +125 -162
- data/lib/redis/client.rb +82 -625
- data/lib/redis/commands/bitmaps.rb +14 -4
- data/lib/redis/commands/cluster.rb +1 -18
- data/lib/redis/commands/connection.rb +5 -10
- data/lib/redis/commands/geo.rb +3 -3
- data/lib/redis/commands/hashes.rb +13 -6
- data/lib/redis/commands/hyper_log_log.rb +1 -1
- data/lib/redis/commands/keys.rb +75 -27
- data/lib/redis/commands/lists.rb +73 -23
- data/lib/redis/commands/pubsub.rb +34 -25
- data/lib/redis/commands/server.rb +15 -15
- data/lib/redis/commands/sets.rb +47 -36
- data/lib/redis/commands/sorted_sets.rb +128 -18
- data/lib/redis/commands/streams.rb +48 -21
- data/lib/redis/commands/strings.rb +18 -17
- data/lib/redis/commands/transactions.rb +7 -31
- data/lib/redis/commands.rb +11 -14
- data/lib/redis/distributed.rb +150 -75
- data/lib/redis/errors.rb +15 -50
- data/lib/redis/hash_ring.rb +26 -26
- data/lib/redis/pipeline.rb +47 -222
- data/lib/redis/subscribe.rb +50 -14
- data/lib/redis/version.rb +1 -1
- data/lib/redis.rb +76 -182
- metadata +10 -57
- data/lib/redis/cluster/command.rb +0 -79
- data/lib/redis/cluster/command_loader.rb +0 -33
- data/lib/redis/cluster/key_slot_converter.rb +0 -72
- data/lib/redis/cluster/node.rb +0 -120
- data/lib/redis/cluster/node_key.rb +0 -31
- data/lib/redis/cluster/node_loader.rb +0 -34
- data/lib/redis/cluster/option.rb +0 -100
- data/lib/redis/cluster/slot.rb +0 -86
- data/lib/redis/cluster/slot_loader.rb +0 -46
- data/lib/redis/cluster.rb +0 -315
- data/lib/redis/connection/command_helper.rb +0 -41
- data/lib/redis/connection/hiredis.rb +0 -66
- data/lib/redis/connection/registry.rb +0 -13
- data/lib/redis/connection/ruby.rb +0 -437
- data/lib/redis/connection/synchrony.rb +0 -148
- data/lib/redis/connection.rb +0 -11
data/lib/redis/pipeline.rb
CHANGED
@@ -4,27 +4,31 @@ require "delegate"
|
|
4
4
|
|
5
5
|
class Redis
|
6
6
|
class PipelinedConnection
|
7
|
-
|
7
|
+
attr_accessor :db
|
8
|
+
|
9
|
+
def initialize(pipeline, futures = [], exception: true)
|
8
10
|
@pipeline = pipeline
|
11
|
+
@futures = futures
|
12
|
+
@exception = exception
|
9
13
|
end
|
10
14
|
|
11
15
|
include Commands
|
12
16
|
|
13
|
-
def db
|
14
|
-
@pipeline.db
|
15
|
-
end
|
16
|
-
|
17
|
-
def db=(db)
|
18
|
-
@pipeline.db = db
|
19
|
-
end
|
20
|
-
|
21
17
|
def pipelined
|
22
18
|
yield self
|
23
19
|
end
|
24
20
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
21
|
+
def multi
|
22
|
+
transaction = MultiConnection.new(@pipeline, @futures)
|
23
|
+
send_command([:multi])
|
24
|
+
size = @futures.size
|
25
|
+
yield transaction
|
26
|
+
multi_future = MultiFuture.new(@futures[size..-1])
|
27
|
+
@pipeline.call_v([:exec]) do |result|
|
28
|
+
multi_future._set(result)
|
29
|
+
end
|
30
|
+
@futures << multi_future
|
31
|
+
multi_future
|
28
32
|
end
|
29
33
|
|
30
34
|
private
|
@@ -34,204 +38,36 @@ class Redis
|
|
34
38
|
end
|
35
39
|
|
36
40
|
def send_command(command, &block)
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
def send_blocking_command(command, timeout, &block)
|
41
|
-
@pipeline.call_with_timeout(command, timeout, &block)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
class Pipeline
|
46
|
-
REDIS_INTERNAL_PATH = File.expand_path("..", __dir__).freeze
|
47
|
-
# Redis use MonitorMixin#synchronize and this class use DelegateClass which we want to filter out.
|
48
|
-
# Both are in the stdlib so we can simply filter the entire stdlib out.
|
49
|
-
STDLIB_PATH = File.expand_path("..", MonitorMixin.instance_method(:synchronize).source_location.first).freeze
|
50
|
-
|
51
|
-
class << self
|
52
|
-
def deprecation_warning(method, caller_locations) # :nodoc:
|
53
|
-
callsite = caller_locations.find { |l| !l.path.start_with?(REDIS_INTERNAL_PATH, STDLIB_PATH) }
|
54
|
-
callsite ||= caller_locations.last # The caller_locations should be large enough, but just in case.
|
55
|
-
::Redis.deprecate! <<~MESSAGE
|
56
|
-
Pipelining commands on a Redis instance is deprecated and will be removed in Redis 5.0.0.
|
57
|
-
|
58
|
-
redis.#{method} do
|
59
|
-
redis.get("key")
|
60
|
-
end
|
61
|
-
|
62
|
-
should be replaced by
|
63
|
-
|
64
|
-
redis.#{method} do |pipeline|
|
65
|
-
pipeline.get("key")
|
66
|
-
end
|
67
|
-
|
68
|
-
(called from #{callsite}}
|
69
|
-
MESSAGE
|
41
|
+
future = Future.new(command, block, @exception)
|
42
|
+
@pipeline.call_v(command) do |result|
|
43
|
+
future._set(result)
|
70
44
|
end
|
71
|
-
end
|
72
|
-
|
73
|
-
attr_accessor :db
|
74
|
-
attr_reader :client
|
75
|
-
|
76
|
-
attr :futures
|
77
|
-
alias materialized_futures futures
|
78
|
-
|
79
|
-
def initialize(client)
|
80
|
-
@client = client.is_a?(Pipeline) ? client.client : client
|
81
|
-
@with_reconnect = true
|
82
|
-
@shutdown = false
|
83
|
-
@futures = []
|
84
|
-
end
|
85
|
-
|
86
|
-
def timeout
|
87
|
-
client.timeout
|
88
|
-
end
|
89
|
-
|
90
|
-
def with_reconnect?
|
91
|
-
@with_reconnect
|
92
|
-
end
|
93
|
-
|
94
|
-
def without_reconnect?
|
95
|
-
!@with_reconnect
|
96
|
-
end
|
97
|
-
|
98
|
-
def shutdown?
|
99
|
-
@shutdown
|
100
|
-
end
|
101
|
-
|
102
|
-
def empty?
|
103
|
-
@futures.empty?
|
104
|
-
end
|
105
|
-
|
106
|
-
def call(command, timeout: nil, &block)
|
107
|
-
# A pipeline that contains a shutdown should not raise ECONNRESET when
|
108
|
-
# the connection is gone.
|
109
|
-
@shutdown = true if command.first == :shutdown
|
110
|
-
future = Future.new(command, block, timeout)
|
111
45
|
@futures << future
|
112
46
|
future
|
113
47
|
end
|
114
48
|
|
115
|
-
def
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
def call_pipeline(pipeline)
|
120
|
-
@shutdown = true if pipeline.shutdown?
|
121
|
-
@futures.concat(pipeline.materialized_futures)
|
122
|
-
@db = pipeline.db
|
123
|
-
nil
|
124
|
-
end
|
125
|
-
|
126
|
-
def commands
|
127
|
-
@futures.map(&:_command)
|
128
|
-
end
|
129
|
-
|
130
|
-
def timeouts
|
131
|
-
@futures.map(&:timeout)
|
132
|
-
end
|
133
|
-
|
134
|
-
def with_reconnect(val = true)
|
135
|
-
@with_reconnect = false unless val
|
136
|
-
yield
|
137
|
-
end
|
138
|
-
|
139
|
-
def without_reconnect(&blk)
|
140
|
-
with_reconnect(false, &blk)
|
141
|
-
end
|
142
|
-
|
143
|
-
def finish(replies, &blk)
|
144
|
-
if blk
|
145
|
-
futures.each_with_index.map do |future, i|
|
146
|
-
future._set(blk.call(replies[i]))
|
147
|
-
end
|
148
|
-
else
|
149
|
-
futures.each_with_index.map do |future, i|
|
150
|
-
future._set(replies[i])
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
class Multi < self
|
156
|
-
def finish(replies)
|
157
|
-
exec = replies.last
|
158
|
-
|
159
|
-
return if exec.nil? # The transaction failed because of WATCH.
|
160
|
-
|
161
|
-
# EXEC command failed.
|
162
|
-
raise exec if exec.is_a?(CommandError)
|
163
|
-
|
164
|
-
if exec.size < futures.size
|
165
|
-
# Some command wasn't recognized by Redis.
|
166
|
-
command_error = replies.detect { |r| r.is_a?(CommandError) }
|
167
|
-
raise command_error
|
168
|
-
end
|
169
|
-
|
170
|
-
super(exec) do |reply|
|
171
|
-
# Because an EXEC returns nested replies, hiredis won't be able to
|
172
|
-
# convert an error reply to a CommandError instance itself. This is
|
173
|
-
# specific to MULTI/EXEC, so we solve this here.
|
174
|
-
reply.is_a?(::RuntimeError) ? CommandError.new(reply.message) : reply
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
def materialized_futures
|
179
|
-
if empty?
|
180
|
-
[]
|
181
|
-
else
|
182
|
-
[
|
183
|
-
Future.new([:multi], nil, 0),
|
184
|
-
*futures,
|
185
|
-
MultiFuture.new(futures)
|
186
|
-
]
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
def timeouts
|
191
|
-
if empty?
|
192
|
-
[]
|
193
|
-
else
|
194
|
-
[nil, *super, nil]
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
def commands
|
199
|
-
if empty?
|
200
|
-
[]
|
201
|
-
else
|
202
|
-
[[:multi]] + super + [[:exec]]
|
203
|
-
end
|
49
|
+
def send_blocking_command(command, timeout, &block)
|
50
|
+
future = Future.new(command, block, @exception)
|
51
|
+
@pipeline.blocking_call_v(timeout, command) do |result|
|
52
|
+
future._set(result)
|
204
53
|
end
|
54
|
+
@futures << future
|
55
|
+
future
|
205
56
|
end
|
206
57
|
end
|
207
58
|
|
208
|
-
class
|
209
|
-
def
|
210
|
-
|
211
|
-
@deprecation_displayed = false
|
59
|
+
class MultiConnection < PipelinedConnection
|
60
|
+
def multi
|
61
|
+
raise Redis::BaseError, "Can't nest multi transaction"
|
212
62
|
end
|
213
63
|
|
214
|
-
|
215
|
-
unless @deprecation_displayed
|
216
|
-
Pipeline.deprecation_warning("pipelined", Kernel.caller_locations(1, 10))
|
217
|
-
@deprecation_displayed = true
|
218
|
-
end
|
219
|
-
@delegate_dc_obj
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
class DeprecatedMulti < DelegateClass(Pipeline::Multi)
|
224
|
-
def initialize(pipeline)
|
225
|
-
super(pipeline)
|
226
|
-
@deprecation_displayed = false
|
227
|
-
end
|
64
|
+
private
|
228
65
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
@delegate_dc_obj
|
66
|
+
# Blocking commands inside transaction behave like non-blocking.
|
67
|
+
# It shouldn't be done though.
|
68
|
+
# https://redis.io/commands/blpop/#blpop-inside-a-multi--exec-transaction
|
69
|
+
def send_blocking_command(command, _timeout, &block)
|
70
|
+
send_command(command, &block)
|
235
71
|
end
|
236
72
|
end
|
237
73
|
|
@@ -244,23 +80,11 @@ class Redis
|
|
244
80
|
class Future < BasicObject
|
245
81
|
FutureNotReady = ::Redis::FutureNotReady.new
|
246
82
|
|
247
|
-
|
248
|
-
|
249
|
-
def initialize(command, transformation, timeout)
|
83
|
+
def initialize(command, coerce, exception)
|
250
84
|
@command = command
|
251
|
-
@transformation = transformation
|
252
|
-
@timeout = timeout
|
253
85
|
@object = FutureNotReady
|
254
|
-
|
255
|
-
|
256
|
-
def ==(_other)
|
257
|
-
message = +"The methods == and != are deprecated for Redis::Future and will be removed in 5.0.0"
|
258
|
-
message << " - You probably meant to call .value == or .value !="
|
259
|
-
message << " (#{::Kernel.caller(1, 1).first})\n"
|
260
|
-
|
261
|
-
::Redis.deprecate!(message)
|
262
|
-
|
263
|
-
super
|
86
|
+
@coerce = coerce
|
87
|
+
@exception = exception
|
264
88
|
end
|
265
89
|
|
266
90
|
def inspect
|
@@ -268,16 +92,12 @@ class Redis
|
|
268
92
|
end
|
269
93
|
|
270
94
|
def _set(object)
|
271
|
-
@object = @
|
95
|
+
@object = @coerce ? @coerce.call(object) : object
|
272
96
|
value
|
273
97
|
end
|
274
98
|
|
275
|
-
def _command
|
276
|
-
@command
|
277
|
-
end
|
278
|
-
|
279
99
|
def value
|
280
|
-
::Kernel.raise(@object) if @object.is_a?(::
|
100
|
+
::Kernel.raise(@object) if @exception && @object.is_a?(::StandardError)
|
281
101
|
@object
|
282
102
|
end
|
283
103
|
|
@@ -294,13 +114,18 @@ class Redis
|
|
294
114
|
def initialize(futures)
|
295
115
|
@futures = futures
|
296
116
|
@command = [:exec]
|
117
|
+
@object = FutureNotReady
|
297
118
|
end
|
298
119
|
|
299
120
|
def _set(replies)
|
300
|
-
@
|
301
|
-
future
|
121
|
+
@object = if replies
|
122
|
+
@futures.map.with_index do |future, index|
|
123
|
+
future._set(replies[index])
|
124
|
+
future.value
|
125
|
+
end
|
126
|
+
else
|
127
|
+
replies
|
302
128
|
end
|
303
|
-
replies
|
304
129
|
end
|
305
130
|
end
|
306
131
|
end
|
data/lib/redis/subscribe.rb
CHANGED
@@ -4,10 +4,13 @@ class Redis
|
|
4
4
|
class SubscribedClient
|
5
5
|
def initialize(client)
|
6
6
|
@client = client
|
7
|
+
@write_monitor = Monitor.new
|
7
8
|
end
|
8
9
|
|
9
|
-
def
|
10
|
-
@
|
10
|
+
def call_v(command)
|
11
|
+
@write_monitor.synchronize do
|
12
|
+
@client.call_v(command)
|
13
|
+
end
|
11
14
|
end
|
12
15
|
|
13
16
|
def subscribe(*channels, &block)
|
@@ -26,12 +29,28 @@ class Redis
|
|
26
29
|
subscription("psubscribe", "punsubscribe", channels, block, timeout)
|
27
30
|
end
|
28
31
|
|
32
|
+
def ssubscribe(*channels, &block)
|
33
|
+
subscription("ssubscribe", "sunsubscribe", channels, block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def ssubscribe_with_timeout(timeout, *channels, &block)
|
37
|
+
subscription("ssubscribe", "sunsubscribe", channels, block, timeout)
|
38
|
+
end
|
39
|
+
|
29
40
|
def unsubscribe(*channels)
|
30
|
-
|
41
|
+
call_v([:unsubscribe, *channels])
|
31
42
|
end
|
32
43
|
|
33
44
|
def punsubscribe(*channels)
|
34
|
-
|
45
|
+
call_v([:punsubscribe, *channels])
|
46
|
+
end
|
47
|
+
|
48
|
+
def sunsubscribe(*channels)
|
49
|
+
call_v([:sunsubscribe, *channels])
|
50
|
+
end
|
51
|
+
|
52
|
+
def close
|
53
|
+
@client.close
|
35
54
|
end
|
36
55
|
|
37
56
|
protected
|
@@ -39,13 +58,21 @@ class Redis
|
|
39
58
|
def subscription(start, stop, channels, block, timeout = 0)
|
40
59
|
sub = Subscription.new(&block)
|
41
60
|
|
42
|
-
|
61
|
+
case start
|
62
|
+
when "ssubscribe" then channels.each { |c| call_v([start, c]) } # avoid cross-slot keys
|
63
|
+
else call_v([start, *channels])
|
64
|
+
end
|
43
65
|
|
44
|
-
@client.
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
66
|
+
while event = @client.next_event(timeout)
|
67
|
+
if event.is_a?(::RedisClient::CommandError)
|
68
|
+
raise Client::ERROR_MAPPING.fetch(event.class), event.message
|
69
|
+
end
|
70
|
+
|
71
|
+
type, *rest = event
|
72
|
+
if callback = sub.callbacks[type]
|
73
|
+
callback.call(*rest)
|
74
|
+
end
|
75
|
+
break if type == stop && rest.last == 0
|
49
76
|
end
|
50
77
|
# No need to unsubscribe here. The real client closes the connection
|
51
78
|
# whenever an exception is raised (see #ensure_connected).
|
@@ -56,10 +83,7 @@ class Redis
|
|
56
83
|
attr :callbacks
|
57
84
|
|
58
85
|
def initialize
|
59
|
-
@callbacks =
|
60
|
-
hash[key] = ->(*_) {}
|
61
|
-
end
|
62
|
-
|
86
|
+
@callbacks = {}
|
63
87
|
yield(self)
|
64
88
|
end
|
65
89
|
|
@@ -86,5 +110,17 @@ class Redis
|
|
86
110
|
def pmessage(&block)
|
87
111
|
@callbacks["pmessage"] = block
|
88
112
|
end
|
113
|
+
|
114
|
+
def ssubscribe(&block)
|
115
|
+
@callbacks["ssubscribe"] = block
|
116
|
+
end
|
117
|
+
|
118
|
+
def sunsubscribe(&block)
|
119
|
+
@callbacks["sunsubscribe"] = block
|
120
|
+
end
|
121
|
+
|
122
|
+
def smessage(&block)
|
123
|
+
@callbacks["smessage"] = block
|
124
|
+
end
|
89
125
|
end
|
90
126
|
end
|
data/lib/redis/version.rb
CHANGED