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.
Files changed (43) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +158 -0
  3. data/README.md +91 -27
  4. data/lib/redis/client.rb +148 -92
  5. data/lib/redis/cluster/command.rb +4 -6
  6. data/lib/redis/cluster/command_loader.rb +6 -7
  7. data/lib/redis/cluster/node.rb +17 -1
  8. data/lib/redis/cluster/node_key.rb +3 -7
  9. data/lib/redis/cluster/option.rb +30 -14
  10. data/lib/redis/cluster/slot.rb +30 -13
  11. data/lib/redis/cluster/slot_loader.rb +4 -4
  12. data/lib/redis/cluster.rb +46 -17
  13. data/lib/redis/commands/bitmaps.rb +63 -0
  14. data/lib/redis/commands/cluster.rb +45 -0
  15. data/lib/redis/commands/connection.rb +58 -0
  16. data/lib/redis/commands/geo.rb +84 -0
  17. data/lib/redis/commands/hashes.rb +251 -0
  18. data/lib/redis/commands/hyper_log_log.rb +37 -0
  19. data/lib/redis/commands/keys.rb +411 -0
  20. data/lib/redis/commands/lists.rb +289 -0
  21. data/lib/redis/commands/pubsub.rb +72 -0
  22. data/lib/redis/commands/scripting.rb +114 -0
  23. data/lib/redis/commands/server.rb +188 -0
  24. data/lib/redis/commands/sets.rb +207 -0
  25. data/lib/redis/commands/sorted_sets.rb +804 -0
  26. data/lib/redis/commands/streams.rb +382 -0
  27. data/lib/redis/commands/strings.rb +313 -0
  28. data/lib/redis/commands/transactions.rb +92 -0
  29. data/lib/redis/commands.rb +242 -0
  30. data/lib/redis/connection/command_helper.rb +5 -2
  31. data/lib/redis/connection/hiredis.rb +7 -5
  32. data/lib/redis/connection/registry.rb +2 -1
  33. data/lib/redis/connection/ruby.rb +129 -110
  34. data/lib/redis/connection/synchrony.rb +17 -10
  35. data/lib/redis/connection.rb +3 -1
  36. data/lib/redis/distributed.rb +209 -70
  37. data/lib/redis/errors.rb +2 -0
  38. data/lib/redis/hash_ring.rb +15 -14
  39. data/lib/redis/pipeline.rb +139 -8
  40. data/lib/redis/subscribe.rb +11 -12
  41. data/lib/redis/version.rb +3 -1
  42. data/lib/redis.rb +167 -3377
  43. metadata +32 -25
@@ -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 { |f| f._command }
121
+ @futures.map(&:_command)
47
122
  end
48
123
 
49
- def with_reconnect(val=true)
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
- raise replies.detect { |r| r.is_a?(CommandError) }
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
- def initialize(command, transformation)
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.kind_of?(::RuntimeError)
262
+ ::Kernel.raise(@object) if @object.is_a?(::RuntimeError)
132
263
  @object
133
264
  end
134
265
 
@@ -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.6.0'
3
5
  end