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
@@ -1,6 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "redis/connection/registry"
|
2
4
|
require "redis/connection/command_helper"
|
3
5
|
require "redis/errors"
|
6
|
+
|
4
7
|
require "socket"
|
5
8
|
require "timeout"
|
6
9
|
|
@@ -10,130 +13,88 @@ rescue LoadError
|
|
10
13
|
# Not all systems have OpenSSL support
|
11
14
|
end
|
12
15
|
|
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
16
|
class Redis
|
24
17
|
module Connection
|
25
18
|
module SocketMixin
|
26
|
-
|
27
|
-
CRLF = "\r\n".freeze
|
28
|
-
|
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
|
19
|
+
CRLF = "\r\n"
|
37
20
|
|
38
21
|
def initialize(*args)
|
39
22
|
super(*args)
|
40
23
|
|
41
24
|
@timeout = @write_timeout = nil
|
42
|
-
@buffer = ""
|
25
|
+
@buffer = "".b
|
43
26
|
end
|
44
27
|
|
45
28
|
def timeout=(timeout)
|
46
|
-
if timeout && timeout > 0
|
47
|
-
@timeout = timeout
|
48
|
-
else
|
49
|
-
@timeout = nil
|
50
|
-
end
|
29
|
+
@timeout = (timeout if timeout && timeout > 0)
|
51
30
|
end
|
52
31
|
|
53
32
|
def write_timeout=(timeout)
|
54
|
-
if timeout && timeout > 0
|
55
|
-
@write_timeout = timeout
|
56
|
-
else
|
57
|
-
@write_timeout = nil
|
58
|
-
end
|
33
|
+
@write_timeout = (timeout if timeout && timeout > 0)
|
59
34
|
end
|
60
35
|
|
61
36
|
def read(nbytes)
|
62
37
|
result = @buffer.slice!(0, nbytes)
|
63
38
|
|
64
|
-
|
65
|
-
|
66
|
-
end
|
39
|
+
buffer = String.new(capacity: nbytes, encoding: Encoding::ASCII_8BIT)
|
40
|
+
result << _read_from_socket(nbytes - result.bytesize, buffer) while result.bytesize < nbytes
|
67
41
|
|
68
42
|
result
|
69
43
|
end
|
70
44
|
|
71
45
|
def gets
|
72
|
-
crlf = nil
|
73
|
-
|
74
|
-
while (crlf = @buffer.index(CRLF)) == nil
|
75
|
-
@buffer << _read_from_socket(1024)
|
46
|
+
while (crlf = @buffer.index(CRLF)).nil?
|
47
|
+
@buffer << _read_from_socket(16_384)
|
76
48
|
end
|
77
49
|
|
78
50
|
@buffer.slice!(0, crlf + CRLF.bytesize)
|
79
51
|
end
|
80
52
|
|
81
|
-
def _read_from_socket(nbytes)
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
raise Redis::TimeoutError
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
rescue EOFError
|
101
|
-
raise Errno::ECONNRESET
|
102
|
-
end
|
103
|
-
|
104
|
-
def _write_to_socket(data)
|
105
|
-
begin
|
106
|
-
write_nonblock(data)
|
107
|
-
|
108
|
-
rescue *NBIO_WRITE_EXCEPTIONS
|
109
|
-
if IO.select(nil, [self], nil, @write_timeout)
|
110
|
-
retry
|
111
|
-
else
|
112
|
-
raise Redis::TimeoutError
|
113
|
-
end
|
114
|
-
rescue *NBIO_READ_EXCEPTIONS
|
115
|
-
if IO.select([self], nil, nil, @write_timeout)
|
116
|
-
retry
|
117
|
-
else
|
118
|
-
raise Redis::TimeoutError
|
53
|
+
def _read_from_socket(nbytes, buffer = nil)
|
54
|
+
loop do
|
55
|
+
case chunk = read_nonblock(nbytes, buffer, exception: false)
|
56
|
+
when :wait_readable
|
57
|
+
unless wait_readable(@timeout)
|
58
|
+
raise Redis::TimeoutError
|
59
|
+
end
|
60
|
+
when :wait_writable
|
61
|
+
unless wait_writable(@timeout)
|
62
|
+
raise Redis::TimeoutError
|
63
|
+
end
|
64
|
+
when nil
|
65
|
+
raise Errno::ECONNRESET
|
66
|
+
when String
|
67
|
+
return chunk
|
119
68
|
end
|
120
69
|
end
|
121
|
-
|
122
|
-
rescue EOFError
|
123
|
-
raise Errno::ECONNRESET
|
124
70
|
end
|
125
71
|
|
126
|
-
def write(
|
127
|
-
return super(
|
72
|
+
def write(buffer)
|
73
|
+
return super(buffer) unless @write_timeout
|
128
74
|
|
129
|
-
|
130
|
-
|
75
|
+
bytes_to_write = buffer.bytesize
|
76
|
+
total_bytes_written = 0
|
131
77
|
loop do
|
132
|
-
|
78
|
+
case bytes_written = write_nonblock(buffer, exception: false)
|
79
|
+
when :wait_readable
|
80
|
+
unless wait_readable(@write_timeout)
|
81
|
+
raise Redis::TimeoutError
|
82
|
+
end
|
83
|
+
when :wait_writable
|
84
|
+
unless wait_writable(@write_timeout)
|
85
|
+
raise Redis::TimeoutError
|
86
|
+
end
|
87
|
+
when nil
|
88
|
+
raise Errno::ECONNRESET
|
89
|
+
when Integer
|
90
|
+
total_bytes_written += bytes_written
|
91
|
+
|
92
|
+
if total_bytes_written >= bytes_to_write
|
93
|
+
return total_bytes_written
|
94
|
+
end
|
133
95
|
|
134
|
-
|
135
|
-
|
136
|
-
data = data.byteslice(count..-1)
|
96
|
+
buffer = buffer.byteslice(bytes_written..-1)
|
97
|
+
end
|
137
98
|
end
|
138
99
|
end
|
139
100
|
end
|
@@ -143,7 +104,6 @@ class Redis
|
|
143
104
|
require "timeout"
|
144
105
|
|
145
106
|
class TCPSocket < ::TCPSocket
|
146
|
-
|
147
107
|
include SocketMixin
|
148
108
|
|
149
109
|
def self.connect(host, port, timeout)
|
@@ -159,7 +119,6 @@ class Redis
|
|
159
119
|
if defined?(::UNIXSocket)
|
160
120
|
|
161
121
|
class UNIXSocket < ::UNIXSocket
|
162
|
-
|
163
122
|
include SocketMixin
|
164
123
|
|
165
124
|
def self.connect(path, timeout)
|
@@ -171,13 +130,14 @@ class Redis
|
|
171
130
|
raise TimeoutError
|
172
131
|
end
|
173
132
|
|
174
|
-
# JRuby raises Errno::EAGAIN on #read_nonblock even when
|
133
|
+
# JRuby raises Errno::EAGAIN on #read_nonblock even when it
|
175
134
|
# says it is readable (1.6.6, in both 1.8 and 1.9 mode).
|
176
135
|
# Use the blocking #readpartial method instead.
|
177
136
|
|
178
|
-
def _read_from_socket(nbytes)
|
137
|
+
def _read_from_socket(nbytes, _buffer = nil)
|
138
|
+
# JRuby: Throw away the buffer as we won't need it
|
139
|
+
# but still need to support the max arity of 2
|
179
140
|
readpartial(nbytes)
|
180
|
-
|
181
141
|
rescue EOFError
|
182
142
|
raise Errno::ECONNRESET
|
183
143
|
end
|
@@ -188,19 +148,16 @@ class Redis
|
|
188
148
|
else
|
189
149
|
|
190
150
|
class TCPSocket < ::Socket
|
191
|
-
|
192
151
|
include SocketMixin
|
193
152
|
|
194
|
-
def self.connect_addrinfo(
|
195
|
-
sock = new(::Socket.const_get(
|
196
|
-
sockaddr = ::Socket.pack_sockaddr_in(port,
|
153
|
+
def self.connect_addrinfo(addrinfo, port, timeout)
|
154
|
+
sock = new(::Socket.const_get(addrinfo[0]), Socket::SOCK_STREAM, 0)
|
155
|
+
sockaddr = ::Socket.pack_sockaddr_in(port, addrinfo[3])
|
197
156
|
|
198
157
|
begin
|
199
158
|
sock.connect_nonblock(sockaddr)
|
200
159
|
rescue Errno::EINPROGRESS
|
201
|
-
|
202
|
-
raise TimeoutError
|
203
|
-
end
|
160
|
+
raise TimeoutError unless sock.wait_writable(timeout)
|
204
161
|
|
205
162
|
begin
|
206
163
|
sock.connect_nonblock(sockaddr)
|
@@ -239,14 +196,13 @@ class Redis
|
|
239
196
|
return connect_addrinfo(ai, port, timeout)
|
240
197
|
rescue SystemCallError
|
241
198
|
# Raise if this was our last attempt.
|
242
|
-
raise if addrinfo.length == i+1
|
199
|
+
raise if addrinfo.length == i + 1
|
243
200
|
end
|
244
201
|
end
|
245
202
|
end
|
246
203
|
end
|
247
204
|
|
248
205
|
class UNIXSocket < ::Socket
|
249
|
-
|
250
206
|
include SocketMixin
|
251
207
|
|
252
208
|
def self.connect(path, timeout)
|
@@ -256,9 +212,7 @@ class Redis
|
|
256
212
|
begin
|
257
213
|
sock.connect_nonblock(sockaddr)
|
258
214
|
rescue Errno::EINPROGRESS
|
259
|
-
|
260
|
-
raise TimeoutError
|
261
|
-
end
|
215
|
+
raise TimeoutError unless sock.wait_writable(timeout)
|
262
216
|
|
263
217
|
begin
|
264
218
|
sock.connect_nonblock(sockaddr)
|
@@ -276,17 +230,58 @@ class Redis
|
|
276
230
|
class SSLSocket < ::OpenSSL::SSL::SSLSocket
|
277
231
|
include SocketMixin
|
278
232
|
|
233
|
+
unless method_defined?(:wait_readable)
|
234
|
+
def wait_readable(timeout = nil)
|
235
|
+
to_io.wait_readable(timeout)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
unless method_defined?(:wait_writable)
|
240
|
+
def wait_writable(timeout = nil)
|
241
|
+
to_io.wait_writable(timeout)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
279
245
|
def self.connect(host, port, timeout, ssl_params)
|
280
|
-
#
|
246
|
+
# NOTE: this is using Redis::Connection::TCPSocket
|
281
247
|
tcp_sock = TCPSocket.connect(host, port, timeout)
|
282
248
|
|
283
249
|
ctx = OpenSSL::SSL::SSLContext.new
|
284
|
-
|
250
|
+
|
251
|
+
# The provided parameters are merged into OpenSSL::SSL::SSLContext::DEFAULT_PARAMS
|
252
|
+
ctx.set_params(ssl_params || {})
|
285
253
|
|
286
254
|
ssl_sock = new(tcp_sock, ctx)
|
287
255
|
ssl_sock.hostname = host
|
288
|
-
|
289
|
-
|
256
|
+
|
257
|
+
begin
|
258
|
+
# Initiate the socket connection in the background. If it doesn't fail
|
259
|
+
# immediately it will raise an IO::WaitWritable (Errno::EINPROGRESS)
|
260
|
+
# indicating the connection is in progress.
|
261
|
+
# Unlike waiting for a tcp socket to connect, you can't time out ssl socket
|
262
|
+
# connections during the connect phase properly, because IO.select only partially works.
|
263
|
+
# Instead, you have to retry.
|
264
|
+
ssl_sock.connect_nonblock
|
265
|
+
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable
|
266
|
+
if ssl_sock.wait_readable(timeout)
|
267
|
+
retry
|
268
|
+
else
|
269
|
+
raise TimeoutError
|
270
|
+
end
|
271
|
+
rescue IO::WaitWritable
|
272
|
+
if ssl_sock.wait_writable(timeout)
|
273
|
+
retry
|
274
|
+
else
|
275
|
+
raise TimeoutError
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
unless ctx.verify_mode == OpenSSL::SSL::VERIFY_NONE || (
|
280
|
+
ctx.respond_to?(:verify_hostname) &&
|
281
|
+
!ctx.verify_hostname
|
282
|
+
)
|
283
|
+
ssl_sock.post_connection_check(host)
|
284
|
+
end
|
290
285
|
|
291
286
|
ssl_sock
|
292
287
|
end
|
@@ -296,31 +291,32 @@ class Redis
|
|
296
291
|
class Ruby
|
297
292
|
include Redis::Connection::CommandHelper
|
298
293
|
|
299
|
-
MINUS = "-"
|
300
|
-
PLUS = "+"
|
301
|
-
COLON = ":"
|
302
|
-
DOLLAR = "$"
|
303
|
-
ASTERISK = "*"
|
294
|
+
MINUS = "-"
|
295
|
+
PLUS = "+"
|
296
|
+
COLON = ":"
|
297
|
+
DOLLAR = "$"
|
298
|
+
ASTERISK = "*"
|
304
299
|
|
305
300
|
def self.connect(config)
|
306
301
|
if config[:scheme] == "unix"
|
307
302
|
raise ArgumentError, "SSL incompatible with unix sockets" if config[:ssl]
|
303
|
+
|
308
304
|
sock = UNIXSocket.connect(config[:path], config[:connect_timeout])
|
309
305
|
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
306
|
sock = SSLSocket.connect(config[:host], config[:port], config[:connect_timeout], config[:ssl_params])
|
312
307
|
else
|
313
308
|
sock = TCPSocket.connect(config[:host], config[:port], config[:connect_timeout])
|
314
309
|
end
|
315
310
|
|
316
311
|
instance = new(sock)
|
317
|
-
instance.timeout = config[:
|
312
|
+
instance.timeout = config[:read_timeout]
|
318
313
|
instance.write_timeout = config[:write_timeout]
|
319
314
|
instance.set_tcp_keepalive config[:tcp_keepalive]
|
315
|
+
instance.set_tcp_nodelay if sock.is_a? TCPSocket
|
320
316
|
instance
|
321
317
|
end
|
322
318
|
|
323
|
-
if [
|
319
|
+
if %i[SOL_SOCKET SO_KEEPALIVE SOL_TCP TCP_KEEPIDLE TCP_KEEPINTVL TCP_KEEPCNT].all? { |c| Socket.const_defined? c }
|
324
320
|
def set_tcp_keepalive(keepalive)
|
325
321
|
return unless keepalive.is_a?(Hash)
|
326
322
|
|
@@ -332,14 +328,13 @@ class Redis
|
|
332
328
|
|
333
329
|
def get_tcp_keepalive
|
334
330
|
{
|
335
|
-
:
|
336
|
-
:
|
337
|
-
:
|
331
|
+
time: @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE).int,
|
332
|
+
intvl: @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL).int,
|
333
|
+
probes: @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT).int
|
338
334
|
}
|
339
335
|
end
|
340
336
|
else
|
341
|
-
def set_tcp_keepalive(keepalive)
|
342
|
-
end
|
337
|
+
def set_tcp_keepalive(keepalive); end
|
343
338
|
|
344
339
|
def get_tcp_keepalive
|
345
340
|
{
|
@@ -347,12 +342,21 @@ class Redis
|
|
347
342
|
end
|
348
343
|
end
|
349
344
|
|
345
|
+
# disables Nagle's Algorithm, prevents multiple round trips with MULTI
|
346
|
+
if %i[IPPROTO_TCP TCP_NODELAY].all? { |c| Socket.const_defined? c }
|
347
|
+
def set_tcp_nodelay
|
348
|
+
@sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
349
|
+
end
|
350
|
+
else
|
351
|
+
def set_tcp_nodelay; end
|
352
|
+
end
|
353
|
+
|
350
354
|
def initialize(sock)
|
351
355
|
@sock = sock
|
352
356
|
end
|
353
357
|
|
354
358
|
def connected?
|
355
|
-
|
359
|
+
!!@sock
|
356
360
|
end
|
357
361
|
|
358
362
|
def disconnect
|
@@ -363,9 +367,7 @@ class Redis
|
|
363
367
|
end
|
364
368
|
|
365
369
|
def timeout=(timeout)
|
366
|
-
if @sock.respond_to?(:timeout=)
|
367
|
-
@sock.timeout = timeout
|
368
|
-
end
|
370
|
+
@sock.timeout = timeout if @sock.respond_to?(:timeout=)
|
369
371
|
end
|
370
372
|
|
371
373
|
def write_timeout=(timeout)
|
@@ -380,9 +382,14 @@ class Redis
|
|
380
382
|
line = @sock.gets
|
381
383
|
reply_type = line.slice!(0, 1)
|
382
384
|
format_reply(reply_type, line)
|
383
|
-
|
384
385
|
rescue Errno::EAGAIN
|
385
386
|
raise TimeoutError
|
387
|
+
rescue OpenSSL::SSL::SSLError => ssl_error
|
388
|
+
if ssl_error.message.match?(/SSL_read: unexpected eof while reading/i)
|
389
|
+
raise EOFError, ssl_error.message
|
390
|
+
else
|
391
|
+
raise
|
392
|
+
end
|
386
393
|
end
|
387
394
|
|
388
395
|
def format_reply(reply_type, line)
|
@@ -392,7 +399,7 @@ class Redis
|
|
392
399
|
when COLON then format_integer_reply(line)
|
393
400
|
when DOLLAR then format_bulk_reply(line)
|
394
401
|
when ASTERISK then format_multi_bulk_reply(line)
|
395
|
-
else raise ProtocolError
|
402
|
+
else raise ProtocolError, reply_type
|
396
403
|
end
|
397
404
|
end
|
398
405
|
|
@@ -411,6 +418,7 @@ class Redis
|
|
411
418
|
def format_bulk_reply(line)
|
412
419
|
bulklen = line.to_i
|
413
420
|
return if bulklen == -1
|
421
|
+
|
414
422
|
reply = encode(@sock.read(bulklen))
|
415
423
|
@sock.read(2) # Discard CRLF.
|
416
424
|
reply
|
@@ -1,9 +1,17 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "redis/connection/registry"
|
4
|
+
require "redis/connection/command_helper"
|
3
5
|
require "redis/errors"
|
6
|
+
|
4
7
|
require "em-synchrony"
|
5
8
|
require "hiredis/reader"
|
6
9
|
|
10
|
+
::Redis.deprecate!(
|
11
|
+
"The redis synchrony driver is deprecated and will be removed in redis-rb 5.0.0. " \
|
12
|
+
"We're looking for people to maintain it as a separate gem, see https://github.com/redis/redis-rb/issues/915"
|
13
|
+
)
|
14
|
+
|
7
15
|
class Redis
|
8
16
|
module Connection
|
9
17
|
class RedisClient < EventMachine::Connection
|
@@ -46,9 +54,7 @@ class Redis
|
|
46
54
|
|
47
55
|
def read
|
48
56
|
@req = EventMachine::DefaultDeferrable.new
|
49
|
-
if @timeout > 0
|
50
|
-
@req.timeout(@timeout, :timeout)
|
51
|
-
end
|
57
|
+
@req.timeout(@timeout, :timeout) if @timeout > 0
|
52
58
|
EventMachine::Synchrony.sync @req
|
53
59
|
end
|
54
60
|
|
@@ -72,7 +78,15 @@ class Redis
|
|
72
78
|
|
73
79
|
def self.connect(config)
|
74
80
|
if config[:scheme] == "unix"
|
75
|
-
|
81
|
+
begin
|
82
|
+
conn = EventMachine.connect_unix_domain(config[:path], RedisClient)
|
83
|
+
rescue RuntimeError => e
|
84
|
+
if e.message == "no connection"
|
85
|
+
raise Errno::ECONNREFUSED
|
86
|
+
else
|
87
|
+
raise e
|
88
|
+
end
|
89
|
+
end
|
76
90
|
elsif config[:scheme] == "rediss" || config[:ssl]
|
77
91
|
raise NotImplementedError, "SSL not supported by synchrony driver"
|
78
92
|
else
|
@@ -97,7 +111,7 @@ class Redis
|
|
97
111
|
end
|
98
112
|
|
99
113
|
def connected?
|
100
|
-
@connection
|
114
|
+
@connection&.connected?
|
101
115
|
end
|
102
116
|
|
103
117
|
def timeout=(timeout)
|
@@ -116,11 +130,12 @@ class Redis
|
|
116
130
|
def read
|
117
131
|
type, payload = @connection.read
|
118
132
|
|
119
|
-
|
133
|
+
case type
|
134
|
+
when :reply
|
120
135
|
payload
|
121
|
-
|
136
|
+
when :error
|
122
137
|
raise payload
|
123
|
-
|
138
|
+
when :timeout
|
124
139
|
raise TimeoutError
|
125
140
|
else
|
126
141
|
raise "Unknown type #{type.inspect}"
|
data/lib/redis/connection.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "redis/connection/registry"
|
2
4
|
|
3
5
|
# If a connection driver was required before this file, the array
|
@@ -6,4 +8,4 @@ require "redis/connection/registry"
|
|
6
8
|
# the plain Ruby driver as our default. Another driver can be required at a
|
7
9
|
# later point in time, causing it to be the last element of the #drivers array
|
8
10
|
# and therefore be chosen by default.
|
9
|
-
|
11
|
+
require_relative "connection/ruby" if Redis::Connection.drivers.empty?
|