redis 4.1.0 → 4.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +40 -0
- data/lib/redis.rb +126 -131
- data/lib/redis/client.rb +37 -13
- data/lib/redis/cluster/slot.rb +1 -1
- data/lib/redis/pipeline.rb +30 -5
- data/lib/redis/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fba6921160b00d34cc11548defceaaee4fc9c181
|
4
|
+
data.tar.gz: e5eac2ace205355e4890084cc96fe98c651e8d30
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5e88bba869f876bc046935479283f108b46107ab00e7ab60487ca524977635dbdc9b0dcfd037a9b057e43765975c26cdc3a0ceea95aefacf608041a18b50f14e
|
7
|
+
data.tar.gz: 91360e891b269c8ecb962414f29d023ace19af98e929d7f7680590dcb8520a87deb6c2a47ba27da0ea16eb47ee90d4c8bdf2be894a40c00820f9c07479188cc2
|
data/README.md
CHANGED
@@ -95,6 +95,46 @@ but a few so that if one is down the client will try the next one. The client
|
|
95
95
|
is able to remember the last Sentinel that was able to reply correctly and will
|
96
96
|
use it for the next requests.
|
97
97
|
|
98
|
+
## Cluster support
|
99
|
+
|
100
|
+
`redis-rb` supports [clustering](https://redis.io/topics/cluster-spec).
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
# Nodes can be passed to the client as an array of connection URLs.
|
104
|
+
nodes = (7000..7005).map { |port| "redis://127.0.0.1:#{port}" }
|
105
|
+
redis = Redis.new(cluster: nodes)
|
106
|
+
|
107
|
+
# You can also specify the options as a Hash. The options are the same as for a single server connection.
|
108
|
+
(7000..7005).map { |port| { host: '127.0.0.1', port: port } }
|
109
|
+
```
|
110
|
+
|
111
|
+
You can also specify only a subset of the nodes, and the client will discover the missing ones using the [CLUSTER NODES](https://redis.io/commands/cluster-nodes) command.
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
Redis.new(cluster: %w[redis://127.0.0.1:7000])
|
115
|
+
```
|
116
|
+
|
117
|
+
If you want [the connection to be able to read from any replica](https://redis.io/commands/readonly), you must pass the `replica: true`. Note that this connection won't be usable to write keys.
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
Redis.new(cluster: nodes, replica: true)
|
121
|
+
```
|
122
|
+
|
123
|
+
The calling code is responsible for [avoiding cross slot commands](https://redis.io/topics/cluster-spec#keys-distribution-model).
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
redis = Redis.new(cluster: %w[redis://127.0.0.1:7000])
|
127
|
+
|
128
|
+
redis.mget('key1', 'key2')
|
129
|
+
#=> Redis::CommandError (CROSSSLOT Keys in request don't hash to the same slot)
|
130
|
+
|
131
|
+
redis.mget('{key}1', '{key}2')
|
132
|
+
#=> [nil, nil]
|
133
|
+
```
|
134
|
+
|
135
|
+
* The client automatically reconnects after a failover occurred, but the caller is responsible for handling errors while it is happening.
|
136
|
+
* The client supports `MOVED` and `ASK` redirections transparently.
|
137
|
+
|
98
138
|
## Storing objects
|
99
139
|
|
100
140
|
Redis only stores strings as values. If you want to store an object, you
|
data/lib/redis.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "monitor"
|
2
4
|
require_relative "redis/errors"
|
3
5
|
|
@@ -104,7 +106,12 @@ class Redis
|
|
104
106
|
def commit
|
105
107
|
synchronize do |client|
|
106
108
|
begin
|
107
|
-
|
109
|
+
pipeline = Pipeline.new(client)
|
110
|
+
@queue[Thread.current.object_id].each do |command|
|
111
|
+
pipeline.call(command)
|
112
|
+
end
|
113
|
+
|
114
|
+
client.call_pipelined(pipeline)
|
108
115
|
ensure
|
109
116
|
@queue.delete(Thread.current.object_id)
|
110
117
|
end
|
@@ -2403,7 +2410,8 @@ class Redis
|
|
2403
2410
|
def pipelined
|
2404
2411
|
synchronize do |client|
|
2405
2412
|
begin
|
2406
|
-
|
2413
|
+
pipeline = Pipeline.new(@client)
|
2414
|
+
original, @client = @client, pipeline
|
2407
2415
|
yield(self)
|
2408
2416
|
original.call_pipeline(@client)
|
2409
2417
|
ensure
|
@@ -2448,7 +2456,7 @@ class Redis
|
|
2448
2456
|
client.call([:multi])
|
2449
2457
|
else
|
2450
2458
|
begin
|
2451
|
-
pipeline = Pipeline::Multi.new
|
2459
|
+
pipeline = Pipeline::Multi.new(@client)
|
2452
2460
|
original, @client = @client, pipeline
|
2453
2461
|
yield(self)
|
2454
2462
|
original.call_pipeline(pipeline)
|
@@ -2818,7 +2826,7 @@ class Redis
|
|
2818
2826
|
# @return [Intger] number of elements added to the sorted set
|
2819
2827
|
def geoadd(key, *member)
|
2820
2828
|
synchronize do |client|
|
2821
|
-
client.call([:geoadd, key, member])
|
2829
|
+
client.call([:geoadd, key, *member])
|
2822
2830
|
end
|
2823
2831
|
end
|
2824
2832
|
|
@@ -2977,24 +2985,6 @@ class Redis
|
|
2977
2985
|
synchronize { |client| client.call(args) }
|
2978
2986
|
end
|
2979
2987
|
|
2980
|
-
# Fetches entries of the stream.
|
2981
|
-
#
|
2982
|
-
# @example Without options
|
2983
|
-
# redis.xrange('mystream')
|
2984
|
-
# @example With first entry id option
|
2985
|
-
# redis.xrange('mystream', first: '0-1')
|
2986
|
-
# @example With first and last entry id options
|
2987
|
-
# redis.xrange('mystream', first: '0-1', last: '0-3')
|
2988
|
-
# @example With count options
|
2989
|
-
# redis.xrange('mystream', count: 10)
|
2990
|
-
#
|
2991
|
-
# @param key [String] the stream key
|
2992
|
-
# @param start [String] first entry id of range, default value is `+`
|
2993
|
-
# @param end [String] last entry id of range, default value is `-`
|
2994
|
-
# @param count [Integer] the number of entries as limit
|
2995
|
-
#
|
2996
|
-
# @return [Hash{String => Hash}] the entries
|
2997
|
-
|
2998
2988
|
# Fetches entries of the stream in ascending order.
|
2999
2989
|
#
|
3000
2990
|
# @example Without options
|
@@ -3327,131 +3317,135 @@ private
|
|
3327
3317
|
# Commands returning 1 for true and 0 for false may be executed in a pipeline
|
3328
3318
|
# where the method call will return nil. Propagate the nil instead of falsely
|
3329
3319
|
# returning false.
|
3330
|
-
Boolify =
|
3331
|
-
|
3332
|
-
|
3333
|
-
|
3320
|
+
Boolify = lambda { |value|
|
3321
|
+
case value
|
3322
|
+
when 1
|
3323
|
+
true
|
3324
|
+
when 0
|
3325
|
+
false
|
3326
|
+
else
|
3327
|
+
value
|
3328
|
+
end
|
3329
|
+
}
|
3334
3330
|
|
3335
|
-
BoolifySet =
|
3336
|
-
|
3337
|
-
|
3338
|
-
|
3339
|
-
|
3340
|
-
|
3341
|
-
|
3342
|
-
|
3331
|
+
BoolifySet = lambda { |value|
|
3332
|
+
case value
|
3333
|
+
when "OK"
|
3334
|
+
true
|
3335
|
+
when nil
|
3336
|
+
false
|
3337
|
+
else
|
3338
|
+
value
|
3339
|
+
end
|
3340
|
+
}
|
3343
3341
|
|
3344
|
-
Hashify =
|
3345
|
-
|
3346
|
-
|
3347
|
-
|
3348
|
-
|
3349
|
-
|
3350
|
-
|
3351
|
-
|
3342
|
+
Hashify = lambda { |value|
|
3343
|
+
if value.respond_to?(:each_slice)
|
3344
|
+
value.each_slice(2).to_h
|
3345
|
+
else
|
3346
|
+
value
|
3347
|
+
end
|
3348
|
+
}
|
3349
|
+
|
3350
|
+
Floatify = lambda { |value|
|
3351
|
+
case value
|
3352
|
+
when "inf"
|
3353
|
+
Float::INFINITY
|
3354
|
+
when "-inf"
|
3355
|
+
-Float::INFINITY
|
3356
|
+
when String
|
3357
|
+
Float(value)
|
3358
|
+
else
|
3359
|
+
value
|
3360
|
+
end
|
3361
|
+
}
|
3352
3362
|
|
3353
|
-
|
3354
|
-
|
3355
|
-
if str
|
3356
|
-
if (inf = str.match(/^(-)?inf/i))
|
3357
|
-
(inf[1] ? -1.0 : 1.0) / 0.0
|
3358
|
-
else
|
3359
|
-
Float(str)
|
3360
|
-
end
|
3361
|
-
end
|
3362
|
-
}
|
3363
|
+
FloatifyPairs = lambda { |value|
|
3364
|
+
return value unless value.respond_to?(:each_slice)
|
3363
3365
|
|
3364
|
-
|
3365
|
-
|
3366
|
-
|
3367
|
-
|
3368
|
-
end
|
3369
|
-
}
|
3366
|
+
value.each_slice(2).map do |member, score|
|
3367
|
+
[member, Floatify.call(score)]
|
3368
|
+
end
|
3369
|
+
}
|
3370
3370
|
|
3371
|
-
HashifyInfo =
|
3372
|
-
|
3373
|
-
|
3374
|
-
|
3375
|
-
|
3376
|
-
|
3371
|
+
HashifyInfo = lambda { |reply|
|
3372
|
+
lines = reply.split("\r\n").grep_v(/^(#|$)/)
|
3373
|
+
lines.map! { |line| line.split(':', 2) }
|
3374
|
+
lines.compact!
|
3375
|
+
lines.to_h
|
3376
|
+
}
|
3377
3377
|
|
3378
|
-
HashifyStreams =
|
3379
|
-
|
3380
|
-
|
3381
|
-
|
3382
|
-
|
3383
|
-
|
3384
|
-
|
3378
|
+
HashifyStreams = lambda { |reply|
|
3379
|
+
case reply
|
3380
|
+
when nil
|
3381
|
+
{}
|
3382
|
+
else
|
3383
|
+
reply.map { |key, entries| [key, HashifyStreamEntries.call(entries)] }.to_h
|
3384
|
+
end
|
3385
|
+
}
|
3385
3386
|
|
3386
|
-
HashifyStreamEntries =
|
3387
|
-
|
3388
|
-
|
3389
|
-
|
3390
|
-
|
3387
|
+
HashifyStreamEntries = lambda { |reply|
|
3388
|
+
reply.map do |entry_id, values|
|
3389
|
+
[entry_id, values.each_slice(2).to_h]
|
3390
|
+
end
|
3391
|
+
}
|
3392
|
+
|
3393
|
+
HashifyStreamPendings = lambda { |reply|
|
3394
|
+
{
|
3395
|
+
'size' => reply[0],
|
3396
|
+
'min_entry_id' => reply[1],
|
3397
|
+
'max_entry_id' => reply[2],
|
3398
|
+
'consumers' => reply[3].nil? ? {} : reply[3].to_h
|
3391
3399
|
}
|
3400
|
+
}
|
3392
3401
|
|
3393
|
-
|
3394
|
-
|
3402
|
+
HashifyStreamPendingDetails = lambda { |reply|
|
3403
|
+
reply.map do |arr|
|
3395
3404
|
{
|
3396
|
-
'
|
3397
|
-
'
|
3398
|
-
'
|
3399
|
-
'
|
3405
|
+
'entry_id' => arr[0],
|
3406
|
+
'consumer' => arr[1],
|
3407
|
+
'elapsed' => arr[2],
|
3408
|
+
'count' => arr[3]
|
3400
3409
|
}
|
3401
|
-
|
3410
|
+
end
|
3411
|
+
}
|
3402
3412
|
|
3403
|
-
|
3404
|
-
|
3405
|
-
|
3406
|
-
|
3407
|
-
|
3408
|
-
|
3409
|
-
|
3410
|
-
|
3411
|
-
|
3412
|
-
|
3413
|
+
HashifyClusterNodeInfo = lambda { |str|
|
3414
|
+
arr = str.split(' ')
|
3415
|
+
{
|
3416
|
+
'node_id' => arr[0],
|
3417
|
+
'ip_port' => arr[1],
|
3418
|
+
'flags' => arr[2].split(','),
|
3419
|
+
'master_node_id' => arr[3],
|
3420
|
+
'ping_sent' => arr[4],
|
3421
|
+
'pong_recv' => arr[5],
|
3422
|
+
'config_epoch' => arr[6],
|
3423
|
+
'link_state' => arr[7],
|
3424
|
+
'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
|
3413
3425
|
}
|
3426
|
+
}
|
3414
3427
|
|
3415
|
-
|
3416
|
-
|
3417
|
-
|
3428
|
+
HashifyClusterSlots = lambda { |reply|
|
3429
|
+
reply.map do |arr|
|
3430
|
+
first_slot, last_slot = arr[0..1]
|
3431
|
+
master = { 'ip' => arr[2][0], 'port' => arr[2][1], 'node_id' => arr[2][2] }
|
3432
|
+
replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
|
3418
3433
|
{
|
3419
|
-
'
|
3420
|
-
'
|
3421
|
-
'
|
3422
|
-
'
|
3423
|
-
'ping_sent' => arr[4],
|
3424
|
-
'pong_recv' => arr[5],
|
3425
|
-
'config_epoch' => arr[6],
|
3426
|
-
'link_state' => arr[7],
|
3427
|
-
'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
|
3434
|
+
'start_slot' => first_slot,
|
3435
|
+
'end_slot' => last_slot,
|
3436
|
+
'master' => master,
|
3437
|
+
'replicas' => replicas
|
3428
3438
|
}
|
3429
|
-
|
3430
|
-
|
3431
|
-
HashifyClusterSlots =
|
3432
|
-
lambda { |reply|
|
3433
|
-
reply.map do |arr|
|
3434
|
-
first_slot, last_slot = arr[0..1]
|
3435
|
-
master = { 'ip' => arr[2][0], 'port' => arr[2][1], 'node_id' => arr[2][2] }
|
3436
|
-
replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
|
3437
|
-
{
|
3438
|
-
'start_slot' => first_slot,
|
3439
|
-
'end_slot' => last_slot,
|
3440
|
-
'master' => master,
|
3441
|
-
'replicas' => replicas
|
3442
|
-
}
|
3443
|
-
end
|
3444
|
-
}
|
3439
|
+
end
|
3440
|
+
}
|
3445
3441
|
|
3446
|
-
HashifyClusterNodes =
|
3447
|
-
|
3448
|
-
|
3449
|
-
}
|
3442
|
+
HashifyClusterNodes = lambda { |reply|
|
3443
|
+
reply.split(/[\r\n]+/).map { |str| HashifyClusterNodeInfo.call(str) }
|
3444
|
+
}
|
3450
3445
|
|
3451
|
-
HashifyClusterSlaves =
|
3452
|
-
|
3453
|
-
|
3454
|
-
}
|
3446
|
+
HashifyClusterSlaves = lambda { |reply|
|
3447
|
+
reply.map { |str| HashifyClusterNodeInfo.call(str) }
|
3448
|
+
}
|
3455
3449
|
|
3456
3450
|
Noop = ->(reply) { reply }
|
3457
3451
|
|
@@ -3459,8 +3453,7 @@ private
|
|
3459
3453
|
args.push sort if sort
|
3460
3454
|
args.push 'count', count if count
|
3461
3455
|
args.push options if options
|
3462
|
-
|
3463
|
-
args.uniq
|
3456
|
+
args
|
3464
3457
|
end
|
3465
3458
|
|
3466
3459
|
def _subscription(method, timeout, channels, block)
|
@@ -3488,6 +3481,8 @@ private
|
|
3488
3481
|
synchronize do |client|
|
3489
3482
|
if blocking_timeout_msec.nil?
|
3490
3483
|
client.call(args, &HashifyStreams)
|
3484
|
+
elsif blocking_timeout_msec.to_f.zero?
|
3485
|
+
client.call_without_timeout(args, &HashifyStreams)
|
3491
3486
|
else
|
3492
3487
|
timeout = client.timeout.to_f + blocking_timeout_msec.to_f / 1000.0
|
3493
3488
|
client.call_with_timeout(args, timeout, &HashifyStreams)
|
data/lib/redis/client.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative "errors"
|
2
4
|
require "socket"
|
3
5
|
require "cgi"
|
@@ -155,12 +157,11 @@ class Redis
|
|
155
157
|
end
|
156
158
|
|
157
159
|
def call_pipeline(pipeline)
|
158
|
-
|
159
|
-
return [] if commands.empty?
|
160
|
+
return [] if pipeline.futures.empty?
|
160
161
|
|
161
162
|
with_reconnect pipeline.with_reconnect? do
|
162
163
|
begin
|
163
|
-
pipeline.finish(call_pipelined(
|
164
|
+
pipeline.finish(call_pipelined(pipeline)).tap do
|
164
165
|
self.db = pipeline.db if pipeline.db
|
165
166
|
end
|
166
167
|
rescue ConnectionError => e
|
@@ -173,8 +174,8 @@ class Redis
|
|
173
174
|
end
|
174
175
|
end
|
175
176
|
|
176
|
-
def call_pipelined(
|
177
|
-
return [] if
|
177
|
+
def call_pipelined(pipeline)
|
178
|
+
return [] if pipeline.futures.empty?
|
178
179
|
|
179
180
|
# The method #ensure_connected (called from #process) reconnects once on
|
180
181
|
# I/O errors. To make an effort in making sure that commands are not
|
@@ -184,6 +185,8 @@ class Redis
|
|
184
185
|
# already successfully executed commands. To circumvent this, don't retry
|
185
186
|
# after the first reply has been read successfully.
|
186
187
|
|
188
|
+
commands = pipeline.commands
|
189
|
+
|
187
190
|
result = Array.new(commands.size)
|
188
191
|
reconnect = @reconnect
|
189
192
|
|
@@ -191,8 +194,12 @@ class Redis
|
|
191
194
|
exception = nil
|
192
195
|
|
193
196
|
process(commands) do
|
194
|
-
|
195
|
-
reply =
|
197
|
+
pipeline.timeouts.each_with_index do |timeout, i|
|
198
|
+
reply = if timeout
|
199
|
+
with_socket_timeout(timeout) { read }
|
200
|
+
else
|
201
|
+
read
|
202
|
+
end
|
196
203
|
result[i] = reply
|
197
204
|
@reconnect = false
|
198
205
|
exception = reply if exception.nil? && reply.is_a?(CommandError)
|
@@ -343,12 +350,14 @@ class Redis
|
|
343
350
|
@pending_reads = 0
|
344
351
|
rescue TimeoutError,
|
345
352
|
SocketError,
|
353
|
+
Errno::EADDRNOTAVAIL,
|
346
354
|
Errno::ECONNREFUSED,
|
347
355
|
Errno::EHOSTDOWN,
|
348
356
|
Errno::EHOSTUNREACH,
|
349
357
|
Errno::ENETUNREACH,
|
350
358
|
Errno::ENOENT,
|
351
|
-
Errno::ETIMEDOUT
|
359
|
+
Errno::ETIMEDOUT,
|
360
|
+
Errno::EINVAL
|
352
361
|
|
353
362
|
raise CannotConnectError, "Error connecting to Redis on #{location} (#{$!.class})"
|
354
363
|
end
|
@@ -407,7 +416,8 @@ class Redis
|
|
407
416
|
options[key] = options[key.to_s] if options.has_key?(key.to_s)
|
408
417
|
end
|
409
418
|
|
410
|
-
url = options[:url]
|
419
|
+
url = options[:url]
|
420
|
+
url = defaults[:url] if url == nil
|
411
421
|
|
412
422
|
# Override defaults from URL if given
|
413
423
|
if url
|
@@ -525,7 +535,6 @@ class Redis
|
|
525
535
|
def initialize(options)
|
526
536
|
super(options)
|
527
537
|
|
528
|
-
@options[:password] = DEFAULTS.fetch(:password)
|
529
538
|
@options[:db] = DEFAULTS.fetch(:db)
|
530
539
|
|
531
540
|
@sentinels = @options.delete(:sentinels).dup
|
@@ -586,6 +595,11 @@ class Redis
|
|
586
595
|
end
|
587
596
|
|
588
597
|
raise CannotConnectError, "No sentinels available."
|
598
|
+
rescue Redis::CommandError => err
|
599
|
+
# this feature is only available starting with Redis 5.0.1
|
600
|
+
raise unless err.message.start_with?('ERR unknown command `auth`')
|
601
|
+
@options[:password] = DEFAULTS.fetch(:password)
|
602
|
+
retry
|
589
603
|
end
|
590
604
|
|
591
605
|
def resolve_master
|
@@ -599,9 +613,19 @@ class Redis
|
|
599
613
|
def resolve_slave
|
600
614
|
sentinel_detect do |client|
|
601
615
|
if reply = client.call(["sentinel", "slaves", @master])
|
602
|
-
|
603
|
-
|
604
|
-
{
|
616
|
+
slaves = reply.map { |s| s.each_slice(2).to_h }
|
617
|
+
slaves.each { |s| s['flags'] = s.fetch('flags').split(',') }
|
618
|
+
slaves.reject! { |s| s.fetch('flags').include?('s_down') }
|
619
|
+
|
620
|
+
if slaves.empty?
|
621
|
+
raise CannotConnectError, 'No slaves available.'
|
622
|
+
else
|
623
|
+
slave = slaves.sample
|
624
|
+
{
|
625
|
+
host: slave.fetch('ip'),
|
626
|
+
port: slave.fetch('port'),
|
627
|
+
}
|
628
|
+
end
|
605
629
|
end
|
606
630
|
end
|
607
631
|
end
|
data/lib/redis/cluster/slot.rb
CHANGED
data/lib/redis/pipeline.rb
CHANGED
@@ -1,15 +1,21 @@
|
|
1
1
|
class Redis
|
2
2
|
class Pipeline
|
3
3
|
attr_accessor :db
|
4
|
+
attr_reader :client
|
4
5
|
|
5
6
|
attr :futures
|
6
7
|
|
7
|
-
def initialize
|
8
|
+
def initialize(client)
|
9
|
+
@client = client.is_a?(Pipeline) ? client.client : client
|
8
10
|
@with_reconnect = true
|
9
11
|
@shutdown = false
|
10
12
|
@futures = []
|
11
13
|
end
|
12
14
|
|
15
|
+
def timeout
|
16
|
+
client.timeout
|
17
|
+
end
|
18
|
+
|
13
19
|
def with_reconnect?
|
14
20
|
@with_reconnect
|
15
21
|
end
|
@@ -26,15 +32,19 @@ class Redis
|
|
26
32
|
@futures.empty?
|
27
33
|
end
|
28
34
|
|
29
|
-
def call(command, &block)
|
35
|
+
def call(command, timeout: nil, &block)
|
30
36
|
# A pipeline that contains a shutdown should not raise ECONNRESET when
|
31
37
|
# the connection is gone.
|
32
38
|
@shutdown = true if command.first == :shutdown
|
33
|
-
future = Future.new(command, block)
|
39
|
+
future = Future.new(command, block, timeout)
|
34
40
|
@futures << future
|
35
41
|
future
|
36
42
|
end
|
37
43
|
|
44
|
+
def call_with_timeout(command, timeout, &block)
|
45
|
+
call(command, timeout: timeout, &block)
|
46
|
+
end
|
47
|
+
|
38
48
|
def call_pipeline(pipeline)
|
39
49
|
@shutdown = true if pipeline.shutdown?
|
40
50
|
@futures.concat(pipeline.futures)
|
@@ -43,7 +53,11 @@ class Redis
|
|
43
53
|
end
|
44
54
|
|
45
55
|
def commands
|
46
|
-
@futures.map
|
56
|
+
@futures.map(&:_command)
|
57
|
+
end
|
58
|
+
|
59
|
+
def timeouts
|
60
|
+
@futures.map(&:timeout)
|
47
61
|
end
|
48
62
|
|
49
63
|
def with_reconnect(val=true)
|
@@ -89,6 +103,14 @@ class Redis
|
|
89
103
|
end
|
90
104
|
end
|
91
105
|
|
106
|
+
def timeouts
|
107
|
+
if empty?
|
108
|
+
[]
|
109
|
+
else
|
110
|
+
[nil, *super, nil]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
92
114
|
def commands
|
93
115
|
if empty?
|
94
116
|
[]
|
@@ -108,9 +130,12 @@ class Redis
|
|
108
130
|
class Future < BasicObject
|
109
131
|
FutureNotReady = ::Redis::FutureNotReady.new
|
110
132
|
|
111
|
-
|
133
|
+
attr_reader :timeout
|
134
|
+
|
135
|
+
def initialize(command, transformation, timeout)
|
112
136
|
@command = command
|
113
137
|
@transformation = transformation
|
138
|
+
@timeout = timeout
|
114
139
|
@object = FutureNotReady
|
115
140
|
end
|
116
141
|
|
data/lib/redis/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.1.
|
4
|
+
version: 4.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ezra Zygmuntowicz
|
@@ -16,7 +16,7 @@ authors:
|
|
16
16
|
autorequire:
|
17
17
|
bindir: bin
|
18
18
|
cert_chain: []
|
19
|
-
date:
|
19
|
+
date: 2019-05-06 00:00:00.000000000 Z
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
22
|
name: test-unit
|