redis 4.1.0 → 4.7.1

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.
Files changed (44) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +171 -0
  3. data/README.md +106 -27
  4. data/lib/redis/client.rb +151 -95
  5. data/lib/redis/cluster/command.rb +4 -6
  6. data/lib/redis/cluster/command_loader.rb +8 -9
  7. data/lib/redis/cluster/node.rb +17 -1
  8. data/lib/redis/cluster/node_key.rb +3 -7
  9. data/lib/redis/cluster/node_loader.rb +8 -11
  10. data/lib/redis/cluster/option.rb +38 -15
  11. data/lib/redis/cluster/slot.rb +30 -13
  12. data/lib/redis/cluster/slot_loader.rb +12 -15
  13. data/lib/redis/cluster.rb +46 -17
  14. data/lib/redis/commands/bitmaps.rb +63 -0
  15. data/lib/redis/commands/cluster.rb +45 -0
  16. data/lib/redis/commands/connection.rb +58 -0
  17. data/lib/redis/commands/geo.rb +84 -0
  18. data/lib/redis/commands/hashes.rb +251 -0
  19. data/lib/redis/commands/hyper_log_log.rb +37 -0
  20. data/lib/redis/commands/keys.rb +411 -0
  21. data/lib/redis/commands/lists.rb +289 -0
  22. data/lib/redis/commands/pubsub.rb +72 -0
  23. data/lib/redis/commands/scripting.rb +114 -0
  24. data/lib/redis/commands/server.rb +188 -0
  25. data/lib/redis/commands/sets.rb +207 -0
  26. data/lib/redis/commands/sorted_sets.rb +812 -0
  27. data/lib/redis/commands/streams.rb +382 -0
  28. data/lib/redis/commands/strings.rb +313 -0
  29. data/lib/redis/commands/transactions.rb +139 -0
  30. data/lib/redis/commands.rb +242 -0
  31. data/lib/redis/connection/command_helper.rb +5 -2
  32. data/lib/redis/connection/hiredis.rb +7 -7
  33. data/lib/redis/connection/registry.rb +2 -1
  34. data/lib/redis/connection/ruby.rb +135 -110
  35. data/lib/redis/connection/synchrony.rb +17 -10
  36. data/lib/redis/connection.rb +3 -1
  37. data/lib/redis/distributed.rb +209 -70
  38. data/lib/redis/errors.rb +11 -0
  39. data/lib/redis/hash_ring.rb +15 -14
  40. data/lib/redis/pipeline.rb +172 -9
  41. data/lib/redis/subscribe.rb +11 -12
  42. data/lib/redis/version.rb +3 -1
  43. data/lib/redis.rb +171 -3375
  44. metadata +32 -25
@@ -1,15 +1,92 @@
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
+ def call_pipeline(pipeline)
26
+ @pipeline.call_pipeline(pipeline)
27
+ nil
28
+ end
29
+
30
+ private
31
+
32
+ def synchronize
33
+ yield self
34
+ end
35
+
36
+ def send_command(command, &block)
37
+ @pipeline.call(command, &block)
38
+ end
39
+
40
+ def send_blocking_command(command, timeout, &block)
41
+ @pipeline.call_with_timeout(command, timeout, &block)
42
+ end
43
+ end
44
+
2
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
70
+ end
71
+ end
72
+
3
73
  attr_accessor :db
74
+ attr_reader :client
4
75
 
5
76
  attr :futures
77
+ alias materialized_futures futures
6
78
 
7
- def initialize
79
+ def initialize(client)
80
+ @client = client.is_a?(Pipeline) ? client.client : client
8
81
  @with_reconnect = true
9
82
  @shutdown = false
10
83
  @futures = []
11
84
  end
12
85
 
86
+ def timeout
87
+ client.timeout
88
+ end
89
+
13
90
  def with_reconnect?
14
91
  @with_reconnect
15
92
  end
@@ -26,27 +103,35 @@ class Redis
26
103
  @futures.empty?
27
104
  end
28
105
 
29
- def call(command, &block)
106
+ def call(command, timeout: nil, &block)
30
107
  # A pipeline that contains a shutdown should not raise ECONNRESET when
31
108
  # the connection is gone.
32
109
  @shutdown = true if command.first == :shutdown
33
- future = Future.new(command, block)
110
+ future = Future.new(command, block, timeout)
34
111
  @futures << future
35
112
  future
36
113
  end
37
114
 
115
+ def call_with_timeout(command, timeout, &block)
116
+ call(command, timeout: timeout, &block)
117
+ end
118
+
38
119
  def call_pipeline(pipeline)
39
120
  @shutdown = true if pipeline.shutdown?
40
- @futures.concat(pipeline.futures)
121
+ @futures.concat(pipeline.materialized_futures)
41
122
  @db = pipeline.db
42
123
  nil
43
124
  end
44
125
 
45
126
  def commands
46
- @futures.map { |f| f._command }
127
+ @futures.map(&:_command)
47
128
  end
48
129
 
49
- def with_reconnect(val=true)
130
+ def timeouts
131
+ @futures.map(&:timeout)
132
+ end
133
+
134
+ def with_reconnect(val = true)
50
135
  @with_reconnect = false unless val
51
136
  yield
52
137
  end
@@ -78,7 +163,8 @@ class Redis
78
163
 
79
164
  if exec.size < futures.size
80
165
  # Some command wasn't recognized by Redis.
81
- raise replies.detect { |r| r.is_a?(CommandError) }
166
+ command_error = replies.detect { |r| r.is_a?(CommandError) }
167
+ raise command_error
82
168
  end
83
169
 
84
170
  super(exec) do |reply|
@@ -89,6 +175,26 @@ class Redis
89
175
  end
90
176
  end
91
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
+
92
198
  def commands
93
199
  if empty?
94
200
  []
@@ -99,6 +205,36 @@ class Redis
99
205
  end
100
206
  end
101
207
 
208
+ class DeprecatedPipeline < DelegateClass(Pipeline)
209
+ def initialize(pipeline)
210
+ super(pipeline)
211
+ @deprecation_displayed = false
212
+ end
213
+
214
+ def __getobj__
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
228
+
229
+ def __getobj__
230
+ unless @deprecation_displayed
231
+ Pipeline.deprecation_warning("multi", Kernel.caller_locations(1, 10))
232
+ @deprecation_displayed = true
233
+ end
234
+ @delegate_dc_obj
235
+ end
236
+ end
237
+
102
238
  class FutureNotReady < RuntimeError
103
239
  def initialize
104
240
  super("Value will be available once the pipeline executes.")
@@ -108,12 +244,25 @@ class Redis
108
244
  class Future < BasicObject
109
245
  FutureNotReady = ::Redis::FutureNotReady.new
110
246
 
111
- def initialize(command, transformation)
247
+ attr_reader :timeout
248
+
249
+ def initialize(command, transformation, timeout)
112
250
  @command = command
113
251
  @transformation = transformation
252
+ @timeout = timeout
114
253
  @object = FutureNotReady
115
254
  end
116
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
264
+ end
265
+
117
266
  def inspect
118
267
  "<Redis::Future #{@command.inspect}>"
119
268
  end
@@ -128,7 +277,7 @@ class Redis
128
277
  end
129
278
 
130
279
  def value
131
- ::Kernel.raise(@object) if @object.kind_of?(::RuntimeError)
280
+ ::Kernel.raise(@object) if @object.is_a?(::RuntimeError)
132
281
  @object
133
282
  end
134
283
 
@@ -140,4 +289,18 @@ class Redis
140
289
  Future
141
290
  end
142
291
  end
292
+
293
+ class MultiFuture < Future
294
+ def initialize(futures)
295
+ @futures = futures
296
+ @command = [:exec]
297
+ end
298
+
299
+ def _set(replies)
300
+ @futures.each_with_index do |future, index|
301
+ future._set(replies[index])
302
+ end
303
+ replies
304
+ end
305
+ end
143
306
  end
@@ -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
- protected
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
- begin
43
- @client.call_loop([start, *channels], timeout) do |line|
44
- type, *rest = line
45
- sub.callbacks[type].call(*rest)
46
- unsubscribed = type == stop && rest.last == 0
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] = lambda { |*_| }
60
+ hash[key] = ->(*_) {}
62
61
  end
63
62
 
64
63
  yield(self)
data/lib/redis/version.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Redis
2
- VERSION = '4.1.0'
4
+ VERSION = '4.7.1'
3
5
  end