redis 3.2.2 → 4.4.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 +175 -13
- data/README.md +223 -76
- data/lib/redis.rb +1360 -445
- data/lib/redis/client.rb +183 -103
- data/lib/redis/cluster.rb +291 -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 +108 -0
- data/lib/redis/cluster/node_key.rb +31 -0
- data/lib/redis/cluster/node_loader.rb +37 -0
- data/lib/redis/cluster/option.rb +93 -0
- data/lib/redis/cluster/slot.rb +86 -0
- data/lib/redis/cluster/slot_loader.rb +49 -0
- data/lib/redis/connection.rb +4 -2
- data/lib/redis/connection/command_helper.rb +5 -10
- data/lib/redis/connection/hiredis.rb +9 -6
- data/lib/redis/connection/registry.rb +2 -1
- data/lib/redis/connection/ruby.rb +168 -63
- data/lib/redis/connection/synchrony.rb +29 -7
- data/lib/redis/distributed.rb +156 -74
- data/lib/redis/errors.rb +48 -0
- data/lib/redis/hash_ring.rb +30 -73
- data/lib/redis/pipeline.rb +55 -15
- data/lib/redis/subscribe.rb +20 -13
- data/lib/redis/version.rb +3 -1
- metadata +41 -170
- data/.gitignore +0 -16
- data/.travis.yml +0 -59
- 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/sentinel.conf +0 -9
- 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/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 -250
- 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 -437
- 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 -125
- 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 -254
- 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/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 -119
- 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 -32
- data/test/transactions_test.rb +0 -264
- data/test/unknown_commands_test.rb +0 -14
- data/test/url_param_test.rb +0 -138
@@ -1,24 +1,27 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "registry"
|
4
|
+
require_relative "../errors"
|
3
5
|
require "hiredis/connection"
|
4
6
|
require "timeout"
|
5
7
|
|
6
8
|
class Redis
|
7
9
|
module Connection
|
8
10
|
class Hiredis
|
9
|
-
|
10
11
|
def self.connect(config)
|
11
12
|
connection = ::Hiredis::Connection.new
|
12
13
|
connect_timeout = (config.fetch(:connect_timeout, 0) * 1_000_000).to_i
|
13
14
|
|
14
15
|
if config[:scheme] == "unix"
|
15
16
|
connection.connect_unix(config[:path], connect_timeout)
|
17
|
+
elsif config[:scheme] == "rediss" || config[:ssl]
|
18
|
+
raise NotImplementedError, "SSL not supported by hiredis driver"
|
16
19
|
else
|
17
20
|
connection.connect(config[:host], config[:port], connect_timeout)
|
18
21
|
end
|
19
22
|
|
20
23
|
instance = new(connection)
|
21
|
-
instance.timeout = config[:
|
24
|
+
instance.timeout = config[:read_timeout]
|
22
25
|
instance
|
23
26
|
rescue Errno::ETIMEDOUT
|
24
27
|
raise TimeoutError
|
@@ -29,7 +32,7 @@ class Redis
|
|
29
32
|
end
|
30
33
|
|
31
34
|
def connected?
|
32
|
-
@connection
|
35
|
+
@connection&.connected?
|
33
36
|
end
|
34
37
|
|
35
38
|
def timeout=(timeout)
|
@@ -55,7 +58,7 @@ class Redis
|
|
55
58
|
rescue Errno::EAGAIN
|
56
59
|
raise TimeoutError
|
57
60
|
rescue RuntimeError => err
|
58
|
-
raise ProtocolError
|
61
|
+
raise ProtocolError, err.message
|
59
62
|
end
|
60
63
|
end
|
61
64
|
end
|
@@ -1,6 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class Redis
|
2
4
|
module Connection
|
3
|
-
|
4
5
|
# Store a list of loaded connection drivers in the Connection module.
|
5
6
|
# Redis::Client uses the last required driver by default, and will be aware
|
6
7
|
# of the loaded connection drivers if the user chooses to override the
|
@@ -1,63 +1,99 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "registry"
|
4
|
+
require_relative "command_helper"
|
5
|
+
require_relative "../errors"
|
4
6
|
require "socket"
|
7
|
+
require "timeout"
|
8
|
+
|
9
|
+
begin
|
10
|
+
require "openssl"
|
11
|
+
rescue LoadError
|
12
|
+
# Not all systems have OpenSSL support
|
13
|
+
end
|
5
14
|
|
6
15
|
class Redis
|
7
16
|
module Connection
|
8
17
|
module SocketMixin
|
9
|
-
|
10
|
-
CRLF = "\r\n".freeze
|
18
|
+
CRLF = "\r\n"
|
11
19
|
|
12
20
|
def initialize(*args)
|
13
21
|
super(*args)
|
14
22
|
|
15
|
-
@timeout = nil
|
16
|
-
@buffer = ""
|
23
|
+
@timeout = @write_timeout = nil
|
24
|
+
@buffer = "".dup
|
17
25
|
end
|
18
26
|
|
19
27
|
def timeout=(timeout)
|
20
|
-
if timeout && timeout > 0
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
28
|
+
@timeout = (timeout if timeout && timeout > 0)
|
29
|
+
end
|
30
|
+
|
31
|
+
def write_timeout=(timeout)
|
32
|
+
@write_timeout = (timeout if timeout && timeout > 0)
|
25
33
|
end
|
26
34
|
|
27
35
|
def read(nbytes)
|
28
36
|
result = @buffer.slice!(0, nbytes)
|
29
37
|
|
30
|
-
while result.bytesize < nbytes
|
31
|
-
result << _read_from_socket(nbytes - result.bytesize)
|
32
|
-
end
|
38
|
+
result << _read_from_socket(nbytes - result.bytesize) while result.bytesize < nbytes
|
33
39
|
|
34
40
|
result
|
35
41
|
end
|
36
42
|
|
37
43
|
def gets
|
38
|
-
crlf = nil
|
39
|
-
|
40
|
-
while (crlf = @buffer.index(CRLF)) == nil
|
41
|
-
@buffer << _read_from_socket(1024)
|
44
|
+
while (crlf = @buffer.index(CRLF)).nil?
|
45
|
+
@buffer << _read_from_socket(16_384)
|
42
46
|
end
|
43
47
|
|
44
48
|
@buffer.slice!(0, crlf + CRLF.bytesize)
|
45
49
|
end
|
46
50
|
|
47
51
|
def _read_from_socket(nbytes)
|
48
|
-
|
49
|
-
read_nonblock(nbytes)
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
52
|
+
loop do
|
53
|
+
case chunk = read_nonblock(nbytes, exception: false)
|
54
|
+
when :wait_readable
|
55
|
+
unless wait_readable(@timeout)
|
56
|
+
raise Redis::TimeoutError
|
57
|
+
end
|
58
|
+
when :wait_writable
|
59
|
+
unless wait_writable(@timeout)
|
60
|
+
raise Redis::TimeoutError
|
61
|
+
end
|
62
|
+
when nil
|
63
|
+
raise Errno::ECONNRESET
|
64
|
+
when String
|
65
|
+
return chunk
|
56
66
|
end
|
57
67
|
end
|
68
|
+
end
|
58
69
|
|
59
|
-
|
60
|
-
|
70
|
+
def write(buffer)
|
71
|
+
return super(buffer) unless @write_timeout
|
72
|
+
|
73
|
+
bytes_to_write = buffer.bytesize
|
74
|
+
total_bytes_written = 0
|
75
|
+
loop do
|
76
|
+
case bytes_written = write_nonblock(buffer, exception: false)
|
77
|
+
when :wait_readable
|
78
|
+
unless wait_readable(@write_timeout)
|
79
|
+
raise Redis::TimeoutError
|
80
|
+
end
|
81
|
+
when :wait_writable
|
82
|
+
unless wait_writable(@write_timeout)
|
83
|
+
raise Redis::TimeoutError
|
84
|
+
end
|
85
|
+
when nil
|
86
|
+
raise Errno::ECONNRESET
|
87
|
+
when Integer
|
88
|
+
total_bytes_written += bytes_written
|
89
|
+
|
90
|
+
if total_bytes_written >= bytes_to_write
|
91
|
+
return total_bytes_written
|
92
|
+
end
|
93
|
+
|
94
|
+
buffer = buffer.byteslice(bytes_written..-1)
|
95
|
+
end
|
96
|
+
end
|
61
97
|
end
|
62
98
|
end
|
63
99
|
|
@@ -66,7 +102,6 @@ class Redis
|
|
66
102
|
require "timeout"
|
67
103
|
|
68
104
|
class TCPSocket < ::TCPSocket
|
69
|
-
|
70
105
|
include SocketMixin
|
71
106
|
|
72
107
|
def self.connect(host, port, timeout)
|
@@ -82,7 +117,6 @@ class Redis
|
|
82
117
|
if defined?(::UNIXSocket)
|
83
118
|
|
84
119
|
class UNIXSocket < ::UNIXSocket
|
85
|
-
|
86
120
|
include SocketMixin
|
87
121
|
|
88
122
|
def self.connect(path, timeout)
|
@@ -94,13 +128,12 @@ class Redis
|
|
94
128
|
raise TimeoutError
|
95
129
|
end
|
96
130
|
|
97
|
-
# JRuby raises Errno::EAGAIN on #read_nonblock even when
|
131
|
+
# JRuby raises Errno::EAGAIN on #read_nonblock even when it
|
98
132
|
# says it is readable (1.6.6, in both 1.8 and 1.9 mode).
|
99
133
|
# Use the blocking #readpartial method instead.
|
100
134
|
|
101
135
|
def _read_from_socket(nbytes)
|
102
136
|
readpartial(nbytes)
|
103
|
-
|
104
137
|
rescue EOFError
|
105
138
|
raise Errno::ECONNRESET
|
106
139
|
end
|
@@ -111,19 +144,16 @@ class Redis
|
|
111
144
|
else
|
112
145
|
|
113
146
|
class TCPSocket < ::Socket
|
114
|
-
|
115
147
|
include SocketMixin
|
116
148
|
|
117
|
-
def self.connect_addrinfo(
|
118
|
-
sock = new(::Socket.const_get(
|
119
|
-
sockaddr = ::Socket.pack_sockaddr_in(port,
|
149
|
+
def self.connect_addrinfo(addrinfo, port, timeout)
|
150
|
+
sock = new(::Socket.const_get(addrinfo[0]), Socket::SOCK_STREAM, 0)
|
151
|
+
sockaddr = ::Socket.pack_sockaddr_in(port, addrinfo[3])
|
120
152
|
|
121
153
|
begin
|
122
154
|
sock.connect_nonblock(sockaddr)
|
123
155
|
rescue Errno::EINPROGRESS
|
124
|
-
|
125
|
-
raise TimeoutError
|
126
|
-
end
|
156
|
+
raise TimeoutError unless sock.wait_writable(timeout)
|
127
157
|
|
128
158
|
begin
|
129
159
|
sock.connect_nonblock(sockaddr)
|
@@ -162,14 +192,13 @@ class Redis
|
|
162
192
|
return connect_addrinfo(ai, port, timeout)
|
163
193
|
rescue SystemCallError
|
164
194
|
# Raise if this was our last attempt.
|
165
|
-
raise if addrinfo.length == i+1
|
195
|
+
raise if addrinfo.length == i + 1
|
166
196
|
end
|
167
197
|
end
|
168
198
|
end
|
169
199
|
end
|
170
200
|
|
171
201
|
class UNIXSocket < ::Socket
|
172
|
-
|
173
202
|
include SocketMixin
|
174
203
|
|
175
204
|
def self.connect(path, timeout)
|
@@ -179,9 +208,7 @@ class Redis
|
|
179
208
|
begin
|
180
209
|
sock.connect_nonblock(sockaddr)
|
181
210
|
rescue Errno::EINPROGRESS
|
182
|
-
|
183
|
-
raise TimeoutError
|
184
|
-
end
|
211
|
+
raise TimeoutError unless sock.wait_writable(timeout)
|
185
212
|
|
186
213
|
begin
|
187
214
|
sock.connect_nonblock(sockaddr)
|
@@ -195,29 +222,97 @@ class Redis
|
|
195
222
|
|
196
223
|
end
|
197
224
|
|
225
|
+
if defined?(OpenSSL)
|
226
|
+
class SSLSocket < ::OpenSSL::SSL::SSLSocket
|
227
|
+
include SocketMixin
|
228
|
+
|
229
|
+
unless method_defined?(:wait_readable)
|
230
|
+
def wait_readable(timeout = nil)
|
231
|
+
to_io.wait_readable(timeout)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
unless method_defined?(:wait_writable)
|
236
|
+
def wait_writable(timeout = nil)
|
237
|
+
to_io.wait_writable(timeout)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def self.connect(host, port, timeout, ssl_params)
|
242
|
+
# Note: this is using Redis::Connection::TCPSocket
|
243
|
+
tcp_sock = TCPSocket.connect(host, port, timeout)
|
244
|
+
|
245
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
246
|
+
|
247
|
+
# The provided parameters are merged into OpenSSL::SSL::SSLContext::DEFAULT_PARAMS
|
248
|
+
ctx.set_params(ssl_params || {})
|
249
|
+
|
250
|
+
ssl_sock = new(tcp_sock, ctx)
|
251
|
+
ssl_sock.hostname = host
|
252
|
+
|
253
|
+
begin
|
254
|
+
# Initiate the socket connection in the background. If it doesn't fail
|
255
|
+
# immediately it will raise an IO::WaitWritable (Errno::EINPROGRESS)
|
256
|
+
# indicating the connection is in progress.
|
257
|
+
# Unlike waiting for a tcp socket to connect, you can't time out ssl socket
|
258
|
+
# connections during the connect phase properly, because IO.select only partially works.
|
259
|
+
# Instead, you have to retry.
|
260
|
+
ssl_sock.connect_nonblock
|
261
|
+
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable
|
262
|
+
if ssl_sock.wait_readable(timeout)
|
263
|
+
retry
|
264
|
+
else
|
265
|
+
raise TimeoutError
|
266
|
+
end
|
267
|
+
rescue IO::WaitWritable
|
268
|
+
if ssl_sock.wait_writable(timeout)
|
269
|
+
retry
|
270
|
+
else
|
271
|
+
raise TimeoutError
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
unless ctx.verify_mode == OpenSSL::SSL::VERIFY_NONE || (
|
276
|
+
ctx.respond_to?(:verify_hostname) &&
|
277
|
+
!ctx.verify_hostname
|
278
|
+
)
|
279
|
+
ssl_sock.post_connection_check(host)
|
280
|
+
end
|
281
|
+
|
282
|
+
ssl_sock
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
198
287
|
class Ruby
|
199
288
|
include Redis::Connection::CommandHelper
|
200
289
|
|
201
|
-
MINUS = "-"
|
202
|
-
PLUS = "+"
|
203
|
-
COLON = ":"
|
204
|
-
DOLLAR = "$"
|
205
|
-
ASTERISK = "*"
|
290
|
+
MINUS = "-"
|
291
|
+
PLUS = "+"
|
292
|
+
COLON = ":"
|
293
|
+
DOLLAR = "$"
|
294
|
+
ASTERISK = "*"
|
206
295
|
|
207
296
|
def self.connect(config)
|
208
297
|
if config[:scheme] == "unix"
|
298
|
+
raise ArgumentError, "SSL incompatible with unix sockets" if config[:ssl]
|
299
|
+
|
209
300
|
sock = UNIXSocket.connect(config[:path], config[:connect_timeout])
|
301
|
+
elsif config[:scheme] == "rediss" || config[:ssl]
|
302
|
+
sock = SSLSocket.connect(config[:host], config[:port], config[:connect_timeout], config[:ssl_params])
|
210
303
|
else
|
211
304
|
sock = TCPSocket.connect(config[:host], config[:port], config[:connect_timeout])
|
212
305
|
end
|
213
306
|
|
214
307
|
instance = new(sock)
|
215
|
-
instance.timeout = config[:
|
308
|
+
instance.timeout = config[:read_timeout]
|
309
|
+
instance.write_timeout = config[:write_timeout]
|
216
310
|
instance.set_tcp_keepalive config[:tcp_keepalive]
|
311
|
+
instance.set_tcp_nodelay if sock.is_a? TCPSocket
|
217
312
|
instance
|
218
313
|
end
|
219
314
|
|
220
|
-
if [
|
315
|
+
if %i[SOL_SOCKET SO_KEEPALIVE SOL_TCP TCP_KEEPIDLE TCP_KEEPINTVL TCP_KEEPCNT].all? { |c| Socket.const_defined? c }
|
221
316
|
def set_tcp_keepalive(keepalive)
|
222
317
|
return unless keepalive.is_a?(Hash)
|
223
318
|
|
@@ -229,14 +324,13 @@ class Redis
|
|
229
324
|
|
230
325
|
def get_tcp_keepalive
|
231
326
|
{
|
232
|
-
:
|
233
|
-
:
|
234
|
-
:
|
327
|
+
time: @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE).int,
|
328
|
+
intvl: @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL).int,
|
329
|
+
probes: @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT).int
|
235
330
|
}
|
236
331
|
end
|
237
332
|
else
|
238
|
-
def set_tcp_keepalive(keepalive)
|
239
|
-
end
|
333
|
+
def set_tcp_keepalive(keepalive); end
|
240
334
|
|
241
335
|
def get_tcp_keepalive
|
242
336
|
{
|
@@ -244,12 +338,21 @@ class Redis
|
|
244
338
|
end
|
245
339
|
end
|
246
340
|
|
341
|
+
# disables Nagle's Algorithm, prevents multiple round trips with MULTI
|
342
|
+
if %i[IPPROTO_TCP TCP_NODELAY].all? { |c| Socket.const_defined? c }
|
343
|
+
def set_tcp_nodelay
|
344
|
+
@sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
345
|
+
end
|
346
|
+
else
|
347
|
+
def set_tcp_nodelay; end
|
348
|
+
end
|
349
|
+
|
247
350
|
def initialize(sock)
|
248
351
|
@sock = sock
|
249
352
|
end
|
250
353
|
|
251
354
|
def connected?
|
252
|
-
|
355
|
+
!!@sock
|
253
356
|
end
|
254
357
|
|
255
358
|
def disconnect
|
@@ -260,9 +363,11 @@ class Redis
|
|
260
363
|
end
|
261
364
|
|
262
365
|
def timeout=(timeout)
|
263
|
-
if @sock.respond_to?(:timeout=)
|
264
|
-
|
265
|
-
|
366
|
+
@sock.timeout = timeout if @sock.respond_to?(:timeout=)
|
367
|
+
end
|
368
|
+
|
369
|
+
def write_timeout=(timeout)
|
370
|
+
@sock.write_timeout = timeout
|
266
371
|
end
|
267
372
|
|
268
373
|
def write(command)
|
@@ -273,7 +378,6 @@ class Redis
|
|
273
378
|
line = @sock.gets
|
274
379
|
reply_type = line.slice!(0, 1)
|
275
380
|
format_reply(reply_type, line)
|
276
|
-
|
277
381
|
rescue Errno::EAGAIN
|
278
382
|
raise TimeoutError
|
279
383
|
end
|
@@ -285,7 +389,7 @@ class Redis
|
|
285
389
|
when COLON then format_integer_reply(line)
|
286
390
|
when DOLLAR then format_bulk_reply(line)
|
287
391
|
when ASTERISK then format_multi_bulk_reply(line)
|
288
|
-
else raise ProtocolError
|
392
|
+
else raise ProtocolError, reply_type
|
289
393
|
end
|
290
394
|
end
|
291
395
|
|
@@ -304,6 +408,7 @@ class Redis
|
|
304
408
|
def format_bulk_reply(line)
|
305
409
|
bulklen = line.to_i
|
306
410
|
return if bulklen == -1
|
411
|
+
|
307
412
|
reply = encode(@sock.read(bulklen))
|
308
413
|
@sock.read(2) # Discard CRLF.
|
309
414
|
reply
|