redis 3.3.5 → 4.1.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 +4 -4
- data/CHANGELOG.md +54 -2
- data/README.md +77 -76
- data/lib/redis.rb +779 -63
- data/lib/redis/client.rb +41 -20
- data/lib/redis/cluster.rb +286 -0
- data/lib/redis/cluster/command.rb +81 -0
- data/lib/redis/cluster/command_loader.rb +34 -0
- data/lib/redis/cluster/key_slot_converter.rb +72 -0
- data/lib/redis/cluster/node.rb +104 -0
- data/lib/redis/cluster/node_key.rb +35 -0
- data/lib/redis/cluster/node_loader.rb +37 -0
- data/lib/redis/cluster/option.rb +77 -0
- data/lib/redis/cluster/slot.rb +69 -0
- data/lib/redis/cluster/slot_loader.rb +49 -0
- data/lib/redis/connection.rb +2 -2
- data/lib/redis/connection/command_helper.rb +2 -8
- data/lib/redis/connection/hiredis.rb +2 -2
- data/lib/redis/connection/ruby.rb +13 -30
- data/lib/redis/connection/synchrony.rb +12 -4
- data/lib/redis/distributed.rb +32 -12
- data/lib/redis/errors.rb +46 -0
- data/lib/redis/hash_ring.rb +20 -64
- data/lib/redis/pipeline.rb +9 -7
- data/lib/redis/version.rb +1 -1
- metadata +53 -196
- data/.gitignore +0 -16
- data/.travis.yml +0 -89
- data/.travis/Gemfile +0 -11
- 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.rb +0 -41
- data/examples/sentinel/start +0 -49
- 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/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/connection.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative "connection/registry"
|
2
2
|
|
3
3
|
# If a connection driver was required before this file, the array
|
4
4
|
# Redis::Connection.drivers will contain one or more classes. The last driver
|
@@ -6,4 +6,4 @@ require "redis/connection/registry"
|
|
6
6
|
# the plain Ruby driver as our default. Another driver can be required at a
|
7
7
|
# later point in time, causing it to be the last element of the #drivers array
|
8
8
|
# and therefore be chosen by default.
|
9
|
-
|
9
|
+
require_relative "connection/ruby" if Redis::Connection.drivers.empty?
|
@@ -30,14 +30,8 @@ class Redis
|
|
30
30
|
|
31
31
|
protected
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
string.force_encoding(Encoding::default_external)
|
36
|
-
end
|
37
|
-
else
|
38
|
-
def encode(string)
|
39
|
-
string
|
40
|
-
end
|
33
|
+
def encode(string)
|
34
|
+
string.force_encoding(Encoding.default_external)
|
41
35
|
end
|
42
36
|
end
|
43
37
|
end
|
@@ -1,6 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require_relative "registry"
|
2
|
+
require_relative "command_helper"
|
3
|
+
require_relative "../errors"
|
4
4
|
require "socket"
|
5
5
|
require "timeout"
|
6
6
|
|
@@ -10,36 +10,17 @@ rescue LoadError
|
|
10
10
|
# Not all systems have OpenSSL support
|
11
11
|
end
|
12
12
|
|
13
|
-
if RUBY_VERSION < "1.9.3"
|
14
|
-
class String
|
15
|
-
# Ruby 1.8.7 does not have byteslice, but it handles encodings differently anyway.
|
16
|
-
# We can simply slice the string, which is a byte array there.
|
17
|
-
def byteslice(*args)
|
18
|
-
slice(*args)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
13
|
class Redis
|
24
14
|
module Connection
|
25
15
|
module SocketMixin
|
26
16
|
|
27
17
|
CRLF = "\r\n".freeze
|
28
18
|
|
29
|
-
# Exceptions raised during non-blocking I/O ops that require retrying the op
|
30
|
-
if RUBY_VERSION >= "1.9.3"
|
31
|
-
NBIO_READ_EXCEPTIONS = [IO::WaitReadable]
|
32
|
-
NBIO_WRITE_EXCEPTIONS = [IO::WaitWritable]
|
33
|
-
else
|
34
|
-
NBIO_READ_EXCEPTIONS = [Errno::EWOULDBLOCK, Errno::EAGAIN]
|
35
|
-
NBIO_WRITE_EXCEPTIONS = [Errno::EWOULDBLOCK, Errno::EAGAIN]
|
36
|
-
end
|
37
|
-
|
38
19
|
def initialize(*args)
|
39
20
|
super(*args)
|
40
21
|
|
41
22
|
@timeout = @write_timeout = nil
|
42
|
-
@buffer = ""
|
23
|
+
@buffer = "".dup
|
43
24
|
end
|
44
25
|
|
45
26
|
def timeout=(timeout)
|
@@ -83,13 +64,13 @@ class Redis
|
|
83
64
|
begin
|
84
65
|
read_nonblock(nbytes)
|
85
66
|
|
86
|
-
rescue
|
67
|
+
rescue IO::WaitReadable
|
87
68
|
if IO.select([self], nil, nil, @timeout)
|
88
69
|
retry
|
89
70
|
else
|
90
71
|
raise Redis::TimeoutError
|
91
72
|
end
|
92
|
-
rescue
|
73
|
+
rescue IO::WaitWritable
|
93
74
|
if IO.select(nil, [self], nil, @timeout)
|
94
75
|
retry
|
95
76
|
else
|
@@ -105,13 +86,13 @@ class Redis
|
|
105
86
|
begin
|
106
87
|
write_nonblock(data)
|
107
88
|
|
108
|
-
rescue
|
89
|
+
rescue IO::WaitWritable
|
109
90
|
if IO.select(nil, [self], nil, @write_timeout)
|
110
91
|
retry
|
111
92
|
else
|
112
93
|
raise Redis::TimeoutError
|
113
94
|
end
|
114
|
-
rescue
|
95
|
+
rescue IO::WaitReadable
|
115
96
|
if IO.select([self], nil, nil, @write_timeout)
|
116
97
|
retry
|
117
98
|
else
|
@@ -286,7 +267,10 @@ class Redis
|
|
286
267
|
ssl_sock = new(tcp_sock, ctx)
|
287
268
|
ssl_sock.hostname = host
|
288
269
|
ssl_sock.connect
|
289
|
-
|
270
|
+
|
271
|
+
unless ctx.verify_mode == OpenSSL::SSL::VERIFY_NONE
|
272
|
+
ssl_sock.post_connection_check(host)
|
273
|
+
end
|
290
274
|
|
291
275
|
ssl_sock
|
292
276
|
end
|
@@ -307,14 +291,13 @@ class Redis
|
|
307
291
|
raise ArgumentError, "SSL incompatible with unix sockets" if config[:ssl]
|
308
292
|
sock = UNIXSocket.connect(config[:path], config[:connect_timeout])
|
309
293
|
elsif config[:scheme] == "rediss" || config[:ssl]
|
310
|
-
raise ArgumentError, "This library does not support SSL on Ruby < 1.9" if RUBY_VERSION < "1.9.3"
|
311
294
|
sock = SSLSocket.connect(config[:host], config[:port], config[:connect_timeout], config[:ssl_params])
|
312
295
|
else
|
313
296
|
sock = TCPSocket.connect(config[:host], config[:port], config[:connect_timeout])
|
314
297
|
end
|
315
298
|
|
316
299
|
instance = new(sock)
|
317
|
-
instance.timeout = config[:
|
300
|
+
instance.timeout = config[:read_timeout]
|
318
301
|
instance.write_timeout = config[:write_timeout]
|
319
302
|
instance.set_tcp_keepalive config[:tcp_keepalive]
|
320
303
|
instance
|
@@ -1,6 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require_relative "command_helper"
|
2
|
+
require_relative "registry"
|
3
|
+
require_relative "../errors"
|
4
4
|
require "em-synchrony"
|
5
5
|
require "hiredis/reader"
|
6
6
|
|
@@ -72,7 +72,15 @@ class Redis
|
|
72
72
|
|
73
73
|
def self.connect(config)
|
74
74
|
if config[:scheme] == "unix"
|
75
|
-
|
75
|
+
begin
|
76
|
+
conn = EventMachine.connect_unix_domain(config[:path], RedisClient)
|
77
|
+
rescue RuntimeError => e
|
78
|
+
if e.message == "no connection"
|
79
|
+
raise Errno::ECONNREFUSED
|
80
|
+
else
|
81
|
+
raise e
|
82
|
+
end
|
83
|
+
end
|
76
84
|
elsif config[:scheme] == "rediss" || config[:ssl]
|
77
85
|
raise NotImplementedError, "SSL not supported by synchrony driver"
|
78
86
|
else
|
data/lib/redis/distributed.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative "hash_ring"
|
2
2
|
|
3
3
|
class Redis
|
4
4
|
class Distributed
|
@@ -144,8 +144,8 @@ class Redis
|
|
144
144
|
end
|
145
145
|
|
146
146
|
# Create a key using the serialized value, previously obtained using DUMP.
|
147
|
-
def restore(key, ttl, serialized_value)
|
148
|
-
node_for(key).restore(key, ttl, serialized_value)
|
147
|
+
def restore(key, ttl, serialized_value, options = {})
|
148
|
+
node_for(key).restore(key, ttl, serialized_value, options)
|
149
149
|
end
|
150
150
|
|
151
151
|
# Transfer a key from the connected instance to another instance.
|
@@ -161,6 +161,14 @@ class Redis
|
|
161
161
|
end
|
162
162
|
end
|
163
163
|
|
164
|
+
# Unlink keys.
|
165
|
+
def unlink(*args)
|
166
|
+
keys_per_node = args.group_by { |key| node_for(key) }
|
167
|
+
keys_per_node.inject(0) do |sum, (node, keys)|
|
168
|
+
sum + node.unlink(*keys)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
164
172
|
# Determine if a key exists.
|
165
173
|
def exists(key)
|
166
174
|
node_for(key).exists(key)
|
@@ -277,13 +285,16 @@ class Redis
|
|
277
285
|
node_for(key).get(key)
|
278
286
|
end
|
279
287
|
|
280
|
-
# Get the values of all the given keys.
|
288
|
+
# Get the values of all the given keys as an Array.
|
281
289
|
def mget(*keys)
|
282
|
-
|
290
|
+
mapped_mget(*keys).values_at(*keys)
|
283
291
|
end
|
284
292
|
|
293
|
+
# Get the values of all the given keys as a Hash.
|
285
294
|
def mapped_mget(*keys)
|
286
|
-
|
295
|
+
keys.group_by { |k| node_for k }.inject({}) do |results, (node, subkeys)|
|
296
|
+
results.merge! node.mapped_mget(*subkeys)
|
297
|
+
end
|
287
298
|
end
|
288
299
|
|
289
300
|
# Overwrite part of a string at key starting at the specified offset.
|
@@ -392,12 +403,11 @@ class Redis
|
|
392
403
|
def _bpop(cmd, args)
|
393
404
|
options = {}
|
394
405
|
|
395
|
-
|
396
|
-
when Hash
|
406
|
+
if args.last.is_a?(Hash)
|
397
407
|
options = args.pop
|
398
|
-
|
408
|
+
elsif args.last.respond_to?(:to_int)
|
399
409
|
# Issue deprecation notice in obnoxious mode...
|
400
|
-
options[:timeout] = args.pop
|
410
|
+
options[:timeout] = args.pop.to_int
|
401
411
|
end
|
402
412
|
|
403
413
|
if args.size > 1
|
@@ -509,6 +519,16 @@ class Redis
|
|
509
519
|
node_for(key).smembers(key)
|
510
520
|
end
|
511
521
|
|
522
|
+
# Scan a set
|
523
|
+
def sscan(key, cursor, options={})
|
524
|
+
node_for(key).sscan(key, cursor, options)
|
525
|
+
end
|
526
|
+
|
527
|
+
# Scan a set and return an enumerator
|
528
|
+
def sscan_each(key, options={}, &block)
|
529
|
+
node_for(key).sscan_each(key, options, &block)
|
530
|
+
end
|
531
|
+
|
512
532
|
# Subtract multiple sets.
|
513
533
|
def sdiff(*keys)
|
514
534
|
ensure_same_node(:sdiff, keys) do |node|
|
@@ -679,8 +699,8 @@ class Redis
|
|
679
699
|
end
|
680
700
|
|
681
701
|
# Delete one or more hash fields.
|
682
|
-
def hdel(key,
|
683
|
-
node_for(key).hdel(key,
|
702
|
+
def hdel(key, *fields)
|
703
|
+
node_for(key).hdel(key, *fields)
|
684
704
|
end
|
685
705
|
|
686
706
|
# Determine if a hash field exists.
|
data/lib/redis/errors.rb
CHANGED
@@ -37,4 +37,50 @@ class Redis
|
|
37
37
|
# Raised when the connection was inherited by a child process.
|
38
38
|
class InheritedError < BaseConnectionError
|
39
39
|
end
|
40
|
+
|
41
|
+
# Raised when client options are invalid.
|
42
|
+
class InvalidClientOptionError < BaseError
|
43
|
+
end
|
44
|
+
|
45
|
+
class Cluster
|
46
|
+
# Raised when client connected to redis as cluster mode
|
47
|
+
# and some cluster subcommands were called.
|
48
|
+
class OrchestrationCommandNotSupported < BaseError
|
49
|
+
def initialize(command, subcommand = '')
|
50
|
+
str = [command, subcommand].map(&:to_s).reject(&:empty?).join(' ').upcase
|
51
|
+
msg = "#{str} command should be used with care "\
|
52
|
+
'only by applications orchestrating Redis Cluster, like redis-trib, '\
|
53
|
+
'and the command if used out of the right context can leave the cluster '\
|
54
|
+
'in a wrong state or cause data loss.'
|
55
|
+
super(msg)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Raised when error occurs on any node of cluster.
|
60
|
+
class CommandErrorCollection < BaseError
|
61
|
+
attr_reader :errors
|
62
|
+
|
63
|
+
# @param errors [Hash{String => Redis::CommandError}]
|
64
|
+
# @param error_message [String]
|
65
|
+
def initialize(errors, error_message = 'Command errors were replied on any node')
|
66
|
+
@errors = errors
|
67
|
+
super(error_message)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Raised when cluster client can't select node.
|
72
|
+
class AmbiguousNodeError < BaseError
|
73
|
+
def initialize(command)
|
74
|
+
super("Cluster client doesn't know which node the #{command} command should be sent to.")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Raised when commands in pipelining include cross slot keys.
|
79
|
+
class CrossSlotPipeliningError < BaseError
|
80
|
+
def initialize(keys)
|
81
|
+
super("Cluster client couldn't send pipelining to single node. "\
|
82
|
+
"The commands include cross slot keys. #{keys}")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
40
86
|
end
|
data/lib/redis/hash_ring.rb
CHANGED
@@ -25,7 +25,6 @@ class Redis
|
|
25
25
|
@nodes << node
|
26
26
|
@replicas.times do |i|
|
27
27
|
key = Zlib.crc32("#{node.id}:#{i}")
|
28
|
-
raise "Node ID collision" if @ring.has_key?(key)
|
29
28
|
@ring[key] = node
|
30
29
|
@sorted_keys << key
|
31
30
|
end
|
@@ -61,72 +60,29 @@ class Redis
|
|
61
60
|
end
|
62
61
|
end
|
63
62
|
|
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
|
101
|
-
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
|
63
|
+
# Find the closest index in HashRing with value <= the given value
|
64
|
+
def self.binary_search(ary, value, &block)
|
65
|
+
upper = ary.size - 1
|
66
|
+
lower = 0
|
67
|
+
idx = 0
|
68
|
+
|
69
|
+
while(lower <= upper) do
|
70
|
+
idx = (lower + upper) / 2
|
71
|
+
comp = ary[idx] <=> value
|
72
|
+
|
73
|
+
if comp == 0
|
74
|
+
return idx
|
75
|
+
elsif comp > 0
|
76
|
+
upper = idx - 1
|
77
|
+
else
|
78
|
+
lower = idx + 1
|
126
79
|
end
|
80
|
+
end
|
127
81
|
|
82
|
+
if upper < 0
|
83
|
+
upper = ary.size - 1
|
128
84
|
end
|
85
|
+
return upper
|
129
86
|
end
|
130
|
-
|
131
87
|
end
|
132
88
|
end
|
data/lib/redis/pipeline.rb
CHANGED
@@ -1,10 +1,4 @@
|
|
1
1
|
class Redis
|
2
|
-
unless defined?(::BasicObject)
|
3
|
-
class BasicObject
|
4
|
-
instance_methods.each { |meth| undef_method(meth) unless meth =~ /\A(__|instance_eval)/ }
|
5
|
-
end
|
6
|
-
end
|
7
|
-
|
8
2
|
class Pipeline
|
9
3
|
attr_accessor :db
|
10
4
|
|
@@ -28,6 +22,10 @@ class Redis
|
|
28
22
|
@shutdown
|
29
23
|
end
|
30
24
|
|
25
|
+
def empty?
|
26
|
+
@futures.empty?
|
27
|
+
end
|
28
|
+
|
31
29
|
def call(command, &block)
|
32
30
|
# A pipeline that contains a shutdown should not raise ECONNRESET when
|
33
31
|
# the connection is gone.
|
@@ -92,7 +90,11 @@ class Redis
|
|
92
90
|
end
|
93
91
|
|
94
92
|
def commands
|
95
|
-
|
93
|
+
if empty?
|
94
|
+
[]
|
95
|
+
else
|
96
|
+
[[:multi]] + super + [[:exec]]
|
97
|
+
end
|
96
98
|
end
|
97
99
|
end
|
98
100
|
end
|