redis 4.1.0 → 4.1.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.
- 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
|