redis 4.6.0 → 5.0.0.beta3
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 +35 -1
- data/README.md +77 -146
- data/lib/redis/client.rb +78 -615
- data/lib/redis/commands/bitmaps.rb +4 -1
- data/lib/redis/commands/cluster.rb +1 -18
- data/lib/redis/commands/connection.rb +5 -10
- data/lib/redis/commands/hashes.rb +6 -3
- data/lib/redis/commands/hyper_log_log.rb +1 -1
- data/lib/redis/commands/keys.rb +52 -26
- data/lib/redis/commands/lists.rb +10 -14
- data/lib/redis/commands/pubsub.rb +7 -9
- data/lib/redis/commands/server.rb +14 -14
- data/lib/redis/commands/sets.rb +42 -35
- data/lib/redis/commands/sorted_sets.rb +13 -3
- data/lib/redis/commands/streams.rb +12 -10
- data/lib/redis/commands/strings.rb +1 -0
- data/lib/redis/commands/transactions.rb +26 -3
- data/lib/redis/commands.rb +1 -8
- data/lib/redis/distributed.rb +99 -66
- data/lib/redis/errors.rb +10 -43
- data/lib/redis/hash_ring.rb +26 -26
- data/lib/redis/pipeline.rb +56 -203
- data/lib/redis/subscribe.rb +15 -9
- data/lib/redis/version.rb +1 -1
- data/lib/redis.rb +69 -175
- metadata +13 -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 -37
- data/lib/redis/cluster/option.rb +0 -93
- data/lib/redis/cluster/slot.rb +0 -86
- data/lib/redis/cluster/slot_loader.rb +0 -49
- data/lib/redis/cluster.rb +0 -315
- data/lib/redis/connection/command_helper.rb +0 -41
- data/lib/redis/connection/hiredis.rb +0 -68
- data/lib/redis/connection/registry.rb +0 -13
- data/lib/redis/connection/ruby.rb +0 -431
- data/lib/redis/connection/synchrony.rb +0 -148
- data/lib/redis/connection.rb +0 -11
data/lib/redis/pipeline.rb
CHANGED
@@ -4,24 +4,32 @@ require "delegate"
|
|
4
4
|
|
5
5
|
class Redis
|
6
6
|
class PipelinedConnection
|
7
|
-
|
7
|
+
attr_accessor :db
|
8
|
+
|
9
|
+
def initialize(pipeline, futures = [])
|
8
10
|
@pipeline = pipeline
|
11
|
+
@futures = futures
|
9
12
|
end
|
10
13
|
|
11
14
|
include Commands
|
12
15
|
|
13
|
-
def db
|
14
|
-
@pipeline.db
|
15
|
-
end
|
16
|
-
|
17
|
-
def db=(db)
|
18
|
-
@pipeline.db = db
|
19
|
-
end
|
20
|
-
|
21
16
|
def pipelined
|
22
17
|
yield self
|
23
18
|
end
|
24
19
|
|
20
|
+
def multi
|
21
|
+
transaction = MultiConnection.new(@pipeline, @futures)
|
22
|
+
send_command([:multi])
|
23
|
+
size = @futures.size
|
24
|
+
yield transaction
|
25
|
+
multi_future = MultiFuture.new(@futures[size..-1])
|
26
|
+
@pipeline.call_v([:exec]) do |result|
|
27
|
+
multi_future._set(result)
|
28
|
+
end
|
29
|
+
@futures << multi_future
|
30
|
+
multi_future
|
31
|
+
end
|
32
|
+
|
25
33
|
private
|
26
34
|
|
27
35
|
def synchronize
|
@@ -29,191 +37,36 @@ class Redis
|
|
29
37
|
end
|
30
38
|
|
31
39
|
def send_command(command, &block)
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
def send_blocking_command(command, timeout, &block)
|
36
|
-
@pipeline.call_with_timeout(command, timeout, &block)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
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
|
40
|
+
future = Future.new(command, block)
|
41
|
+
@pipeline.call_v(command) do |result|
|
42
|
+
future._set(result)
|
65
43
|
end
|
66
|
-
end
|
67
|
-
|
68
|
-
attr_accessor :db
|
69
|
-
attr_reader :client
|
70
|
-
|
71
|
-
attr :futures
|
72
|
-
|
73
|
-
def initialize(client)
|
74
|
-
@client = client.is_a?(Pipeline) ? client.client : client
|
75
|
-
@with_reconnect = true
|
76
|
-
@shutdown = false
|
77
|
-
@futures = []
|
78
|
-
end
|
79
|
-
|
80
|
-
def timeout
|
81
|
-
client.timeout
|
82
|
-
end
|
83
|
-
|
84
|
-
def with_reconnect?
|
85
|
-
@with_reconnect
|
86
|
-
end
|
87
|
-
|
88
|
-
def without_reconnect?
|
89
|
-
!@with_reconnect
|
90
|
-
end
|
91
|
-
|
92
|
-
def shutdown?
|
93
|
-
@shutdown
|
94
|
-
end
|
95
|
-
|
96
|
-
def empty?
|
97
|
-
@futures.empty?
|
98
|
-
end
|
99
|
-
|
100
|
-
def call(command, timeout: nil, &block)
|
101
|
-
# A pipeline that contains a shutdown should not raise ECONNRESET when
|
102
|
-
# the connection is gone.
|
103
|
-
@shutdown = true if command.first == :shutdown
|
104
|
-
future = Future.new(command, block, timeout)
|
105
44
|
@futures << future
|
106
45
|
future
|
107
46
|
end
|
108
47
|
|
109
|
-
def
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
def call_pipeline(pipeline)
|
114
|
-
@shutdown = true if pipeline.shutdown?
|
115
|
-
@futures.concat(pipeline.futures)
|
116
|
-
@db = pipeline.db
|
117
|
-
nil
|
118
|
-
end
|
119
|
-
|
120
|
-
def commands
|
121
|
-
@futures.map(&:_command)
|
122
|
-
end
|
123
|
-
|
124
|
-
def timeouts
|
125
|
-
@futures.map(&:timeout)
|
126
|
-
end
|
127
|
-
|
128
|
-
def with_reconnect(val = true)
|
129
|
-
@with_reconnect = false unless val
|
130
|
-
yield
|
131
|
-
end
|
132
|
-
|
133
|
-
def without_reconnect(&blk)
|
134
|
-
with_reconnect(false, &blk)
|
135
|
-
end
|
136
|
-
|
137
|
-
def finish(replies, &blk)
|
138
|
-
if blk
|
139
|
-
futures.each_with_index.map do |future, i|
|
140
|
-
future._set(blk.call(replies[i]))
|
141
|
-
end
|
142
|
-
else
|
143
|
-
futures.each_with_index.map do |future, i|
|
144
|
-
future._set(replies[i])
|
145
|
-
end
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
class Multi < self
|
150
|
-
def finish(replies)
|
151
|
-
exec = replies.last
|
152
|
-
|
153
|
-
return if exec.nil? # The transaction failed because of WATCH.
|
154
|
-
|
155
|
-
# EXEC command failed.
|
156
|
-
raise exec if exec.is_a?(CommandError)
|
157
|
-
|
158
|
-
if exec.size < futures.size
|
159
|
-
# Some command wasn't recognized by Redis.
|
160
|
-
command_error = replies.detect { |r| r.is_a?(CommandError) }
|
161
|
-
raise command_error
|
162
|
-
end
|
163
|
-
|
164
|
-
super(exec) do |reply|
|
165
|
-
# Because an EXEC returns nested replies, hiredis won't be able to
|
166
|
-
# convert an error reply to a CommandError instance itself. This is
|
167
|
-
# specific to MULTI/EXEC, so we solve this here.
|
168
|
-
reply.is_a?(::RuntimeError) ? CommandError.new(reply.message) : reply
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
def timeouts
|
173
|
-
if empty?
|
174
|
-
[]
|
175
|
-
else
|
176
|
-
[nil, *super, nil]
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
def commands
|
181
|
-
if empty?
|
182
|
-
[]
|
183
|
-
else
|
184
|
-
[[:multi]] + super + [[:exec]]
|
185
|
-
end
|
48
|
+
def send_blocking_command(command, timeout, &block)
|
49
|
+
future = Future.new(command, block)
|
50
|
+
@pipeline.blocking_call_v(timeout, command) do |result|
|
51
|
+
future._set(result)
|
186
52
|
end
|
53
|
+
@futures << future
|
54
|
+
future
|
187
55
|
end
|
188
56
|
end
|
189
57
|
|
190
|
-
class
|
191
|
-
def
|
192
|
-
|
193
|
-
@deprecation_displayed = false
|
58
|
+
class MultiConnection < PipelinedConnection
|
59
|
+
def multi
|
60
|
+
raise Redis::Error, "Can't nest multi transaction"
|
194
61
|
end
|
195
62
|
|
196
|
-
|
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
|
63
|
+
private
|
204
64
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
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
|
65
|
+
# Blocking commands inside transaction behave like non-blocking.
|
66
|
+
# It shouldn't be done though.
|
67
|
+
# https://redis.io/commands/blpop/#blpop-inside-a-multi--exec-transaction
|
68
|
+
def send_blocking_command(command, _timeout, &block)
|
69
|
+
send_command(command, &block)
|
217
70
|
end
|
218
71
|
end
|
219
72
|
|
@@ -226,23 +79,10 @@ class Redis
|
|
226
79
|
class Future < BasicObject
|
227
80
|
FutureNotReady = ::Redis::FutureNotReady.new
|
228
81
|
|
229
|
-
|
230
|
-
|
231
|
-
def initialize(command, transformation, timeout)
|
82
|
+
def initialize(command, coerce)
|
232
83
|
@command = command
|
233
|
-
@transformation = transformation
|
234
|
-
@timeout = timeout
|
235
84
|
@object = FutureNotReady
|
236
|
-
|
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
|
85
|
+
@coerce = coerce
|
246
86
|
end
|
247
87
|
|
248
88
|
def inspect
|
@@ -250,16 +90,12 @@ class Redis
|
|
250
90
|
end
|
251
91
|
|
252
92
|
def _set(object)
|
253
|
-
@object = @
|
93
|
+
@object = @coerce ? @coerce.call(object) : object
|
254
94
|
value
|
255
95
|
end
|
256
96
|
|
257
|
-
def _command
|
258
|
-
@command
|
259
|
-
end
|
260
|
-
|
261
97
|
def value
|
262
|
-
::Kernel.raise(@object) if @object.is_a?(::
|
98
|
+
::Kernel.raise(@object) if @object.is_a?(::StandardError)
|
263
99
|
@object
|
264
100
|
end
|
265
101
|
|
@@ -271,4 +107,21 @@ class Redis
|
|
271
107
|
Future
|
272
108
|
end
|
273
109
|
end
|
110
|
+
|
111
|
+
class MultiFuture < Future
|
112
|
+
def initialize(futures)
|
113
|
+
@futures = futures
|
114
|
+
@command = [:exec]
|
115
|
+
@object = FutureNotReady
|
116
|
+
end
|
117
|
+
|
118
|
+
def _set(replies)
|
119
|
+
if replies
|
120
|
+
@futures.each_with_index do |future, index|
|
121
|
+
future._set(replies[index])
|
122
|
+
end
|
123
|
+
end
|
124
|
+
@object = replies
|
125
|
+
end
|
126
|
+
end
|
274
127
|
end
|
data/lib/redis/subscribe.rb
CHANGED
@@ -6,8 +6,8 @@ class Redis
|
|
6
6
|
@client = client
|
7
7
|
end
|
8
8
|
|
9
|
-
def
|
10
|
-
@client.
|
9
|
+
def call_v(command)
|
10
|
+
@client.call_v(command)
|
11
11
|
end
|
12
12
|
|
13
13
|
def subscribe(*channels, &block)
|
@@ -27,11 +27,15 @@ class Redis
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def unsubscribe(*channels)
|
30
|
-
|
30
|
+
call_v([:unsubscribe, *channels])
|
31
31
|
end
|
32
32
|
|
33
33
|
def punsubscribe(*channels)
|
34
|
-
|
34
|
+
call_v([:punsubscribe, *channels])
|
35
|
+
end
|
36
|
+
|
37
|
+
def close
|
38
|
+
@client.close
|
35
39
|
end
|
36
40
|
|
37
41
|
protected
|
@@ -39,13 +43,15 @@ class Redis
|
|
39
43
|
def subscription(start, stop, channels, block, timeout = 0)
|
40
44
|
sub = Subscription.new(&block)
|
41
45
|
|
42
|
-
|
46
|
+
@client.call_v([start, *channels])
|
47
|
+
while event = @client.next_event(timeout)
|
48
|
+
if event.is_a?(::RedisClient::CommandError)
|
49
|
+
raise Client::ERROR_MAPPING.fetch(event.class), event.message
|
50
|
+
end
|
43
51
|
|
44
|
-
|
45
|
-
type, *rest = line
|
52
|
+
type, *rest = event
|
46
53
|
sub.callbacks[type].call(*rest)
|
47
|
-
|
48
|
-
break if unsubscribed
|
54
|
+
break if type == stop && rest.last == 0
|
49
55
|
end
|
50
56
|
# No need to unsubscribe here. The real client closes the connection
|
51
57
|
# whenever an exception is raised (see #ensure_connected).
|
data/lib/redis/version.rb
CHANGED