redis 3.3.5 → 4.8.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 +232 -2
- data/README.md +169 -89
- data/lib/redis/client.rb +177 -100
- data/lib/redis/cluster/command.rb +79 -0
- data/lib/redis/cluster/command_loader.rb +33 -0
- data/lib/redis/cluster/key_slot_converter.rb +72 -0
- data/lib/redis/cluster/node.rb +120 -0
- data/lib/redis/cluster/node_key.rb +31 -0
- data/lib/redis/cluster/node_loader.rb +34 -0
- data/lib/redis/cluster/option.rb +100 -0
- data/lib/redis/cluster/slot.rb +86 -0
- data/lib/redis/cluster/slot_loader.rb +46 -0
- data/lib/redis/cluster.rb +315 -0
- 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 +455 -0
- data/lib/redis/commands/lists.rb +290 -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 +223 -0
- data/lib/redis/commands/sorted_sets.rb +812 -0
- data/lib/redis/commands/streams.rb +382 -0
- data/lib/redis/commands/strings.rb +313 -0
- data/lib/redis/commands/transactions.rb +139 -0
- data/lib/redis/commands.rb +240 -0
- data/lib/redis/connection/command_helper.rb +7 -10
- data/lib/redis/connection/hiredis.rb +5 -3
- data/lib/redis/connection/registry.rb +2 -1
- data/lib/redis/connection/ruby.rb +136 -128
- data/lib/redis/connection/synchrony.rb +24 -9
- data/lib/redis/connection.rb +3 -1
- data/lib/redis/distributed.rb +255 -85
- data/lib/redis/errors.rb +57 -0
- data/lib/redis/hash_ring.rb +30 -73
- data/lib/redis/pipeline.rb +178 -13
- data/lib/redis/subscribe.rb +11 -12
- data/lib/redis/version.rb +3 -1
- data/lib/redis.rb +174 -2661
- metadata +66 -202
- data/.gitignore +0 -16
- data/.travis/Gemfile +0 -11
- data/.travis.yml +0 -89
- data/.yardopts +0 -3
- data/Gemfile +0 -4
- data/Rakefile +0 -87
- data/benchmarking/logging.rb +0 -71
- data/benchmarking/pipeline.rb +0 -51
- data/benchmarking/speed.rb +0 -21
- data/benchmarking/suite.rb +0 -24
- data/benchmarking/worker.rb +0 -71
- data/examples/basic.rb +0 -15
- data/examples/consistency.rb +0 -114
- data/examples/dist_redis.rb +0 -43
- data/examples/incr-decr.rb +0 -17
- data/examples/list.rb +0 -26
- data/examples/pubsub.rb +0 -37
- data/examples/sentinel/sentinel.conf +0 -9
- data/examples/sentinel/start +0 -49
- data/examples/sentinel.rb +0 -41
- data/examples/sets.rb +0 -36
- data/examples/unicorn/config.ru +0 -3
- data/examples/unicorn/unicorn.rb +0 -20
- data/redis.gemspec +0 -44
- data/test/bitpos_test.rb +0 -69
- data/test/blocking_commands_test.rb +0 -42
- data/test/client_test.rb +0 -59
- data/test/command_map_test.rb +0 -30
- data/test/commands_on_hashes_test.rb +0 -21
- data/test/commands_on_hyper_log_log_test.rb +0 -21
- data/test/commands_on_lists_test.rb +0 -20
- data/test/commands_on_sets_test.rb +0 -77
- data/test/commands_on_sorted_sets_test.rb +0 -137
- data/test/commands_on_strings_test.rb +0 -101
- data/test/commands_on_value_types_test.rb +0 -133
- data/test/connection_handling_test.rb +0 -277
- data/test/connection_test.rb +0 -57
- data/test/db/.gitkeep +0 -0
- data/test/distributed_blocking_commands_test.rb +0 -46
- data/test/distributed_commands_on_hashes_test.rb +0 -10
- data/test/distributed_commands_on_hyper_log_log_test.rb +0 -33
- data/test/distributed_commands_on_lists_test.rb +0 -22
- data/test/distributed_commands_on_sets_test.rb +0 -83
- data/test/distributed_commands_on_sorted_sets_test.rb +0 -18
- data/test/distributed_commands_on_strings_test.rb +0 -59
- data/test/distributed_commands_on_value_types_test.rb +0 -95
- data/test/distributed_commands_requiring_clustering_test.rb +0 -164
- data/test/distributed_connection_handling_test.rb +0 -23
- data/test/distributed_internals_test.rb +0 -79
- data/test/distributed_key_tags_test.rb +0 -52
- data/test/distributed_persistence_control_commands_test.rb +0 -26
- data/test/distributed_publish_subscribe_test.rb +0 -92
- data/test/distributed_remote_server_control_commands_test.rb +0 -66
- data/test/distributed_scripting_test.rb +0 -102
- data/test/distributed_sorting_test.rb +0 -20
- data/test/distributed_test.rb +0 -58
- data/test/distributed_transactions_test.rb +0 -32
- data/test/encoding_test.rb +0 -18
- data/test/error_replies_test.rb +0 -59
- data/test/fork_safety_test.rb +0 -65
- data/test/helper.rb +0 -232
- data/test/helper_test.rb +0 -24
- data/test/internals_test.rb +0 -417
- data/test/lint/blocking_commands.rb +0 -150
- data/test/lint/hashes.rb +0 -162
- data/test/lint/hyper_log_log.rb +0 -60
- data/test/lint/lists.rb +0 -143
- data/test/lint/sets.rb +0 -140
- data/test/lint/sorted_sets.rb +0 -316
- data/test/lint/strings.rb +0 -260
- data/test/lint/value_types.rb +0 -122
- data/test/persistence_control_commands_test.rb +0 -26
- data/test/pipelining_commands_test.rb +0 -242
- data/test/publish_subscribe_test.rb +0 -282
- data/test/remote_server_control_commands_test.rb +0 -118
- data/test/scanning_test.rb +0 -413
- data/test/scripting_test.rb +0 -78
- data/test/sentinel_command_test.rb +0 -80
- data/test/sentinel_test.rb +0 -255
- data/test/sorting_test.rb +0 -59
- data/test/ssl_test.rb +0 -73
- data/test/support/connection/hiredis.rb +0 -1
- data/test/support/connection/ruby.rb +0 -1
- data/test/support/connection/synchrony.rb +0 -17
- data/test/support/redis_mock.rb +0 -130
- data/test/support/ssl/gen_certs.sh +0 -31
- data/test/support/ssl/trusted-ca.crt +0 -25
- data/test/support/ssl/trusted-ca.key +0 -27
- data/test/support/ssl/trusted-cert.crt +0 -81
- data/test/support/ssl/trusted-cert.key +0 -28
- data/test/support/ssl/untrusted-ca.crt +0 -26
- data/test/support/ssl/untrusted-ca.key +0 -27
- data/test/support/ssl/untrusted-cert.crt +0 -82
- data/test/support/ssl/untrusted-cert.key +0 -28
- data/test/support/wire/synchrony.rb +0 -24
- data/test/support/wire/thread.rb +0 -5
- data/test/synchrony_driver.rb +0 -88
- data/test/test.conf.erb +0 -9
- data/test/thread_safety_test.rb +0 -62
- data/test/transactions_test.rb +0 -264
- data/test/unknown_commands_test.rb +0 -14
- data/test/url_param_test.rb +0 -138
data/lib/redis/hash_ring.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'zlib'
|
2
4
|
|
3
5
|
class Redis
|
4
6
|
class HashRing
|
5
|
-
|
6
7
|
POINTS_PER_SERVER = 160 # this is the default in libmemcached
|
7
8
|
|
8
9
|
attr_reader :ring, :sorted_keys, :replicas, :nodes
|
@@ -10,7 +11,7 @@ class Redis
|
|
10
11
|
# nodes is a list of objects that have a proper to_s representation.
|
11
12
|
# replicas indicates how many virtual points should be used pr. node,
|
12
13
|
# replicas are required to improve the distribution.
|
13
|
-
def initialize(nodes=[], replicas=POINTS_PER_SERVER)
|
14
|
+
def initialize(nodes = [], replicas = POINTS_PER_SERVER)
|
14
15
|
@replicas = replicas
|
15
16
|
@ring = {}
|
16
17
|
@nodes = []
|
@@ -25,7 +26,6 @@ class Redis
|
|
25
26
|
@nodes << node
|
26
27
|
@replicas.times do |i|
|
27
28
|
key = Zlib.crc32("#{node.id}:#{i}")
|
28
|
-
raise "Node ID collision" if @ring.has_key?(key)
|
29
29
|
@ring[key] = node
|
30
30
|
@sorted_keys << key
|
31
31
|
end
|
@@ -33,11 +33,11 @@ class Redis
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def remove_node(node)
|
36
|
-
@nodes.reject!{|n| n.id == node.id}
|
36
|
+
@nodes.reject! { |n| n.id == node.id }
|
37
37
|
@replicas.times do |i|
|
38
38
|
key = Zlib.crc32("#{node.id}:#{i}")
|
39
39
|
@ring.delete(key)
|
40
|
-
@sorted_keys.reject! {|k| k == key}
|
40
|
+
@sorted_keys.reject! { |k| k == key }
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
@@ -47,86 +47,43 @@ class Redis
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def get_node_pos(key)
|
50
|
-
return [nil,nil] if @ring.
|
50
|
+
return [nil, nil] if @ring.empty?
|
51
|
+
|
51
52
|
crc = Zlib.crc32(key)
|
52
53
|
idx = HashRing.binary_search(@sorted_keys, crc)
|
53
|
-
|
54
|
+
[@ring[@sorted_keys[idx]], idx]
|
54
55
|
end
|
55
56
|
|
56
57
|
def iter_nodes(key)
|
57
|
-
return [nil,nil] if @ring.
|
58
|
+
return [nil, nil] if @ring.empty?
|
59
|
+
|
58
60
|
_, pos = get_node_pos(key)
|
59
61
|
@ring.size.times do |n|
|
60
|
-
yield @ring[@sorted_keys[(pos+n) % @ring.size]]
|
62
|
+
yield @ring[@sorted_keys[(pos + n) % @ring.size]]
|
61
63
|
end
|
62
64
|
end
|
63
65
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
while (lower <= upper) {
|
81
|
-
idx = (lower + upper) / 2;
|
82
|
-
|
83
|
-
VALUE continuumValue = RARRAY_PTR(ary)[idx];
|
84
|
-
unsigned int l = NUM2UINT(continuumValue);
|
85
|
-
if (l == r) {
|
86
|
-
return idx;
|
87
|
-
}
|
88
|
-
else if (l > r) {
|
89
|
-
upper = idx - 1;
|
90
|
-
}
|
91
|
-
else {
|
92
|
-
lower = idx + 1;
|
93
|
-
}
|
94
|
-
}
|
95
|
-
if (upper < 0) {
|
96
|
-
upper = RARRAY_LEN(ary) - 1;
|
97
|
-
}
|
98
|
-
return upper;
|
99
|
-
}
|
100
|
-
EOM
|
66
|
+
# Find the closest index in HashRing with value <= the given value
|
67
|
+
def self.binary_search(ary, value)
|
68
|
+
upper = ary.size - 1
|
69
|
+
lower = 0
|
70
|
+
idx = 0
|
71
|
+
|
72
|
+
while lower <= upper
|
73
|
+
idx = (lower + upper) / 2
|
74
|
+
comp = ary[idx] <=> value
|
75
|
+
|
76
|
+
if comp == 0
|
77
|
+
return idx
|
78
|
+
elsif comp > 0
|
79
|
+
upper = idx - 1
|
80
|
+
else
|
81
|
+
lower = idx + 1
|
101
82
|
end
|
102
|
-
rescue Exception
|
103
|
-
# Find the closest index in HashRing with value <= the given value
|
104
|
-
def binary_search(ary, value, &block)
|
105
|
-
upper = ary.size - 1
|
106
|
-
lower = 0
|
107
|
-
idx = 0
|
108
|
-
|
109
|
-
while(lower <= upper) do
|
110
|
-
idx = (lower + upper) / 2
|
111
|
-
comp = ary[idx] <=> value
|
112
|
-
|
113
|
-
if comp == 0
|
114
|
-
return idx
|
115
|
-
elsif comp > 0
|
116
|
-
upper = idx - 1
|
117
|
-
else
|
118
|
-
lower = idx + 1
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
if upper < 0
|
123
|
-
upper = ary.size - 1
|
124
|
-
end
|
125
|
-
return upper
|
126
|
-
end
|
127
|
-
|
128
83
|
end
|
129
|
-
end
|
130
84
|
|
85
|
+
upper = ary.size - 1 if upper < 0
|
86
|
+
upper
|
87
|
+
end
|
131
88
|
end
|
132
89
|
end
|
data/lib/redis/pipeline.rb
CHANGED
@@ -1,21 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "delegate"
|
4
|
+
|
1
5
|
class Redis
|
2
|
-
|
3
|
-
|
4
|
-
|
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)
|
5
42
|
end
|
6
43
|
end
|
7
44
|
|
8
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
|
+
|
9
73
|
attr_accessor :db
|
74
|
+
attr_reader :client
|
10
75
|
|
11
76
|
attr :futures
|
77
|
+
alias materialized_futures futures
|
12
78
|
|
13
|
-
def initialize
|
79
|
+
def initialize(client)
|
80
|
+
@client = client.is_a?(Pipeline) ? client.client : client
|
14
81
|
@with_reconnect = true
|
15
82
|
@shutdown = false
|
16
83
|
@futures = []
|
17
84
|
end
|
18
85
|
|
86
|
+
def timeout
|
87
|
+
client.timeout
|
88
|
+
end
|
89
|
+
|
19
90
|
def with_reconnect?
|
20
91
|
@with_reconnect
|
21
92
|
end
|
@@ -28,27 +99,39 @@ class Redis
|
|
28
99
|
@shutdown
|
29
100
|
end
|
30
101
|
|
31
|
-
def
|
102
|
+
def empty?
|
103
|
+
@futures.empty?
|
104
|
+
end
|
105
|
+
|
106
|
+
def call(command, timeout: nil, &block)
|
32
107
|
# A pipeline that contains a shutdown should not raise ECONNRESET when
|
33
108
|
# the connection is gone.
|
34
109
|
@shutdown = true if command.first == :shutdown
|
35
|
-
future = Future.new(command, block)
|
110
|
+
future = Future.new(command, block, timeout)
|
36
111
|
@futures << future
|
37
112
|
future
|
38
113
|
end
|
39
114
|
|
115
|
+
def call_with_timeout(command, timeout, &block)
|
116
|
+
call(command, timeout: timeout, &block)
|
117
|
+
end
|
118
|
+
|
40
119
|
def call_pipeline(pipeline)
|
41
120
|
@shutdown = true if pipeline.shutdown?
|
42
|
-
@futures.concat(pipeline.
|
121
|
+
@futures.concat(pipeline.materialized_futures)
|
43
122
|
@db = pipeline.db
|
44
123
|
nil
|
45
124
|
end
|
46
125
|
|
47
126
|
def commands
|
48
|
-
@futures.map
|
127
|
+
@futures.map(&:_command)
|
128
|
+
end
|
129
|
+
|
130
|
+
def timeouts
|
131
|
+
@futures.map(&:timeout)
|
49
132
|
end
|
50
133
|
|
51
|
-
def with_reconnect(val=true)
|
134
|
+
def with_reconnect(val = true)
|
52
135
|
@with_reconnect = false unless val
|
53
136
|
yield
|
54
137
|
end
|
@@ -80,7 +163,8 @@ class Redis
|
|
80
163
|
|
81
164
|
if exec.size < futures.size
|
82
165
|
# Some command wasn't recognized by Redis.
|
83
|
-
|
166
|
+
command_error = replies.detect { |r| r.is_a?(CommandError) }
|
167
|
+
raise command_error
|
84
168
|
end
|
85
169
|
|
86
170
|
super(exec) do |reply|
|
@@ -91,9 +175,63 @@ class Redis
|
|
91
175
|
end
|
92
176
|
end
|
93
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
|
+
|
94
198
|
def commands
|
95
|
-
|
199
|
+
if empty?
|
200
|
+
[]
|
201
|
+
else
|
202
|
+
[[:multi]] + super + [[:exec]]
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
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
|
96
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
|
97
235
|
end
|
98
236
|
end
|
99
237
|
|
@@ -106,12 +244,25 @@ class Redis
|
|
106
244
|
class Future < BasicObject
|
107
245
|
FutureNotReady = ::Redis::FutureNotReady.new
|
108
246
|
|
109
|
-
|
247
|
+
attr_reader :timeout
|
248
|
+
|
249
|
+
def initialize(command, transformation, timeout)
|
110
250
|
@command = command
|
111
251
|
@transformation = transformation
|
252
|
+
@timeout = timeout
|
112
253
|
@object = FutureNotReady
|
113
254
|
end
|
114
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
|
+
|
115
266
|
def inspect
|
116
267
|
"<Redis::Future #{@command.inspect}>"
|
117
268
|
end
|
@@ -126,7 +277,7 @@ class Redis
|
|
126
277
|
end
|
127
278
|
|
128
279
|
def value
|
129
|
-
::Kernel.raise(@object) if @object.
|
280
|
+
::Kernel.raise(@object) if @object.is_a?(::RuntimeError)
|
130
281
|
@object
|
131
282
|
end
|
132
283
|
|
@@ -138,4 +289,18 @@ class Redis
|
|
138
289
|
Future
|
139
290
|
end
|
140
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
|
141
306
|
end
|
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