redis 3.2.0 → 4.6.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 +278 -15
- data/README.md +260 -76
- data/lib/redis/client.rb +239 -115
- 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 +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/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 +411 -0
- data/lib/redis/commands/lists.rb +289 -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 +207 -0
- data/lib/redis/commands/sorted_sets.rb +804 -0
- data/lib/redis/commands/streams.rb +382 -0
- data/lib/redis/commands/strings.rb +313 -0
- data/lib/redis/commands/transactions.rb +92 -0
- data/lib/redis/commands.rb +242 -0
- data/lib/redis/connection/command_helper.rb +7 -10
- data/lib/redis/connection/hiredis.rb +11 -6
- data/lib/redis/connection/registry.rb +2 -1
- data/lib/redis/connection/ruby.rb +173 -64
- data/lib/redis/connection/synchrony.rb +32 -8
- data/lib/redis/connection.rb +3 -1
- data/lib/redis/distributed.rb +233 -74
- data/lib/redis/errors.rb +48 -0
- data/lib/redis/hash_ring.rb +30 -72
- data/lib/redis/pipeline.rb +145 -12
- data/lib/redis/subscribe.rb +20 -13
- data/lib/redis/version.rb +3 -1
- data/lib/redis.rb +171 -2476
- metadata +71 -165
- data/.gitignore +0 -15
- data/.travis/Gemfile +0 -11
- data/.travis.yml +0 -54
- data/.yardopts +0 -3
- data/Gemfile +0 -4
- data/Rakefile +0 -68
- 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 -43
- 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 -123
- data/test/commands_on_strings_test.rb +0 -101
- data/test/commands_on_value_types_test.rb +0 -131
- data/test/connection_handling_test.rb +0 -189
- 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 -70
- 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 -434
- 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 -238
- 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 -210
- data/test/remote_server_control_commands_test.rb +0 -117
- data/test/scanning_test.rb +0 -413
- data/test/scripting_test.rb +0 -78
- 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 -115
- 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 +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 -132
@@ -1,63 +1,101 @@
|
|
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"
|
8
|
+
require "timeout"
|
9
|
+
|
10
|
+
begin
|
11
|
+
require "openssl"
|
12
|
+
rescue LoadError
|
13
|
+
# Not all systems have OpenSSL support
|
14
|
+
end
|
5
15
|
|
6
16
|
class Redis
|
7
17
|
module Connection
|
8
18
|
module SocketMixin
|
9
|
-
|
10
|
-
CRLF = "\r\n".freeze
|
19
|
+
CRLF = "\r\n"
|
11
20
|
|
12
21
|
def initialize(*args)
|
13
22
|
super(*args)
|
14
23
|
|
15
|
-
@timeout = nil
|
16
|
-
@buffer = ""
|
24
|
+
@timeout = @write_timeout = nil
|
25
|
+
@buffer = "".b
|
17
26
|
end
|
18
27
|
|
19
28
|
def timeout=(timeout)
|
20
|
-
if timeout && timeout > 0
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
29
|
+
@timeout = (timeout if timeout && timeout > 0)
|
30
|
+
end
|
31
|
+
|
32
|
+
def write_timeout=(timeout)
|
33
|
+
@write_timeout = (timeout if timeout && timeout > 0)
|
25
34
|
end
|
26
35
|
|
27
36
|
def read(nbytes)
|
28
37
|
result = @buffer.slice!(0, nbytes)
|
29
38
|
|
30
|
-
|
31
|
-
|
32
|
-
end
|
39
|
+
buffer = String.new(capacity: nbytes, encoding: Encoding::ASCII_8BIT)
|
40
|
+
result << _read_from_socket(nbytes - result.bytesize, buffer) while result.bytesize < nbytes
|
33
41
|
|
34
42
|
result
|
35
43
|
end
|
36
44
|
|
37
45
|
def gets
|
38
|
-
crlf = nil
|
39
|
-
|
40
|
-
while (crlf = @buffer.index(CRLF)) == nil
|
41
|
-
@buffer << _read_from_socket(1024)
|
46
|
+
while (crlf = @buffer.index(CRLF)).nil?
|
47
|
+
@buffer << _read_from_socket(16_384)
|
42
48
|
end
|
43
49
|
|
44
50
|
@buffer.slice!(0, crlf + CRLF.bytesize)
|
45
51
|
end
|
46
52
|
|
47
|
-
def _read_from_socket(nbytes)
|
48
|
-
|
49
|
-
read_nonblock(nbytes)
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
56
68
|
end
|
57
69
|
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def write(buffer)
|
73
|
+
return super(buffer) unless @write_timeout
|
74
|
+
|
75
|
+
bytes_to_write = buffer.bytesize
|
76
|
+
total_bytes_written = 0
|
77
|
+
loop do
|
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
|
58
95
|
|
59
|
-
|
60
|
-
|
96
|
+
buffer = buffer.byteslice(bytes_written..-1)
|
97
|
+
end
|
98
|
+
end
|
61
99
|
end
|
62
100
|
end
|
63
101
|
|
@@ -66,7 +104,6 @@ class Redis
|
|
66
104
|
require "timeout"
|
67
105
|
|
68
106
|
class TCPSocket < ::TCPSocket
|
69
|
-
|
70
107
|
include SocketMixin
|
71
108
|
|
72
109
|
def self.connect(host, port, timeout)
|
@@ -82,7 +119,6 @@ class Redis
|
|
82
119
|
if defined?(::UNIXSocket)
|
83
120
|
|
84
121
|
class UNIXSocket < ::UNIXSocket
|
85
|
-
|
86
122
|
include SocketMixin
|
87
123
|
|
88
124
|
def self.connect(path, timeout)
|
@@ -94,13 +130,14 @@ class Redis
|
|
94
130
|
raise TimeoutError
|
95
131
|
end
|
96
132
|
|
97
|
-
# JRuby raises Errno::EAGAIN on #read_nonblock even when
|
133
|
+
# JRuby raises Errno::EAGAIN on #read_nonblock even when it
|
98
134
|
# says it is readable (1.6.6, in both 1.8 and 1.9 mode).
|
99
135
|
# Use the blocking #readpartial method instead.
|
100
136
|
|
101
|
-
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
|
102
140
|
readpartial(nbytes)
|
103
|
-
|
104
141
|
rescue EOFError
|
105
142
|
raise Errno::ECONNRESET
|
106
143
|
end
|
@@ -111,19 +148,16 @@ class Redis
|
|
111
148
|
else
|
112
149
|
|
113
150
|
class TCPSocket < ::Socket
|
114
|
-
|
115
151
|
include SocketMixin
|
116
152
|
|
117
|
-
def self.connect_addrinfo(
|
118
|
-
sock = new(::Socket.const_get(
|
119
|
-
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])
|
120
156
|
|
121
157
|
begin
|
122
158
|
sock.connect_nonblock(sockaddr)
|
123
159
|
rescue Errno::EINPROGRESS
|
124
|
-
|
125
|
-
raise TimeoutError
|
126
|
-
end
|
160
|
+
raise TimeoutError unless sock.wait_writable(timeout)
|
127
161
|
|
128
162
|
begin
|
129
163
|
sock.connect_nonblock(sockaddr)
|
@@ -162,14 +196,13 @@ class Redis
|
|
162
196
|
return connect_addrinfo(ai, port, timeout)
|
163
197
|
rescue SystemCallError
|
164
198
|
# Raise if this was our last attempt.
|
165
|
-
raise if addrinfo.length == i+1
|
199
|
+
raise if addrinfo.length == i + 1
|
166
200
|
end
|
167
201
|
end
|
168
202
|
end
|
169
203
|
end
|
170
204
|
|
171
205
|
class UNIXSocket < ::Socket
|
172
|
-
|
173
206
|
include SocketMixin
|
174
207
|
|
175
208
|
def self.connect(path, timeout)
|
@@ -179,9 +212,7 @@ class Redis
|
|
179
212
|
begin
|
180
213
|
sock.connect_nonblock(sockaddr)
|
181
214
|
rescue Errno::EINPROGRESS
|
182
|
-
|
183
|
-
raise TimeoutError
|
184
|
-
end
|
215
|
+
raise TimeoutError unless sock.wait_writable(timeout)
|
185
216
|
|
186
217
|
begin
|
187
218
|
sock.connect_nonblock(sockaddr)
|
@@ -195,29 +226,97 @@ class Redis
|
|
195
226
|
|
196
227
|
end
|
197
228
|
|
229
|
+
if defined?(OpenSSL)
|
230
|
+
class SSLSocket < ::OpenSSL::SSL::SSLSocket
|
231
|
+
include SocketMixin
|
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
|
+
|
245
|
+
def self.connect(host, port, timeout, ssl_params)
|
246
|
+
# NOTE: this is using Redis::Connection::TCPSocket
|
247
|
+
tcp_sock = TCPSocket.connect(host, port, timeout)
|
248
|
+
|
249
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
250
|
+
|
251
|
+
# The provided parameters are merged into OpenSSL::SSL::SSLContext::DEFAULT_PARAMS
|
252
|
+
ctx.set_params(ssl_params || {})
|
253
|
+
|
254
|
+
ssl_sock = new(tcp_sock, ctx)
|
255
|
+
ssl_sock.hostname = host
|
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
|
285
|
+
|
286
|
+
ssl_sock
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
198
291
|
class Ruby
|
199
292
|
include Redis::Connection::CommandHelper
|
200
293
|
|
201
|
-
MINUS = "-"
|
202
|
-
PLUS = "+"
|
203
|
-
COLON = ":"
|
204
|
-
DOLLAR = "$"
|
205
|
-
ASTERISK = "*"
|
294
|
+
MINUS = "-"
|
295
|
+
PLUS = "+"
|
296
|
+
COLON = ":"
|
297
|
+
DOLLAR = "$"
|
298
|
+
ASTERISK = "*"
|
206
299
|
|
207
300
|
def self.connect(config)
|
208
301
|
if config[:scheme] == "unix"
|
209
|
-
|
302
|
+
raise ArgumentError, "SSL incompatible with unix sockets" if config[:ssl]
|
303
|
+
|
304
|
+
sock = UNIXSocket.connect(config[:path], config[:connect_timeout])
|
305
|
+
elsif config[:scheme] == "rediss" || config[:ssl]
|
306
|
+
sock = SSLSocket.connect(config[:host], config[:port], config[:connect_timeout], config[:ssl_params])
|
210
307
|
else
|
211
|
-
sock = TCPSocket.connect(config[:host], config[:port], config[:
|
308
|
+
sock = TCPSocket.connect(config[:host], config[:port], config[:connect_timeout])
|
212
309
|
end
|
213
310
|
|
214
311
|
instance = new(sock)
|
215
|
-
instance.timeout = config[:
|
312
|
+
instance.timeout = config[:read_timeout]
|
313
|
+
instance.write_timeout = config[:write_timeout]
|
216
314
|
instance.set_tcp_keepalive config[:tcp_keepalive]
|
315
|
+
instance.set_tcp_nodelay if sock.is_a? TCPSocket
|
217
316
|
instance
|
218
317
|
end
|
219
318
|
|
220
|
-
if [
|
319
|
+
if %i[SOL_SOCKET SO_KEEPALIVE SOL_TCP TCP_KEEPIDLE TCP_KEEPINTVL TCP_KEEPCNT].all? { |c| Socket.const_defined? c }
|
221
320
|
def set_tcp_keepalive(keepalive)
|
222
321
|
return unless keepalive.is_a?(Hash)
|
223
322
|
|
@@ -229,14 +328,13 @@ class Redis
|
|
229
328
|
|
230
329
|
def get_tcp_keepalive
|
231
330
|
{
|
232
|
-
:
|
233
|
-
:
|
234
|
-
:
|
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
|
235
334
|
}
|
236
335
|
end
|
237
336
|
else
|
238
|
-
def set_tcp_keepalive(keepalive)
|
239
|
-
end
|
337
|
+
def set_tcp_keepalive(keepalive); end
|
240
338
|
|
241
339
|
def get_tcp_keepalive
|
242
340
|
{
|
@@ -244,12 +342,21 @@ class Redis
|
|
244
342
|
end
|
245
343
|
end
|
246
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
|
+
|
247
354
|
def initialize(sock)
|
248
355
|
@sock = sock
|
249
356
|
end
|
250
357
|
|
251
358
|
def connected?
|
252
|
-
|
359
|
+
!!@sock
|
253
360
|
end
|
254
361
|
|
255
362
|
def disconnect
|
@@ -260,9 +367,11 @@ class Redis
|
|
260
367
|
end
|
261
368
|
|
262
369
|
def timeout=(timeout)
|
263
|
-
if @sock.respond_to?(:timeout=)
|
264
|
-
|
265
|
-
|
370
|
+
@sock.timeout = timeout if @sock.respond_to?(:timeout=)
|
371
|
+
end
|
372
|
+
|
373
|
+
def write_timeout=(timeout)
|
374
|
+
@sock.write_timeout = timeout
|
266
375
|
end
|
267
376
|
|
268
377
|
def write(command)
|
@@ -273,7 +382,6 @@ class Redis
|
|
273
382
|
line = @sock.gets
|
274
383
|
reply_type = line.slice!(0, 1)
|
275
384
|
format_reply(reply_type, line)
|
276
|
-
|
277
385
|
rescue Errno::EAGAIN
|
278
386
|
raise TimeoutError
|
279
387
|
end
|
@@ -285,7 +393,7 @@ class Redis
|
|
285
393
|
when COLON then format_integer_reply(line)
|
286
394
|
when DOLLAR then format_bulk_reply(line)
|
287
395
|
when ASTERISK then format_multi_bulk_reply(line)
|
288
|
-
else raise ProtocolError
|
396
|
+
else raise ProtocolError, reply_type
|
289
397
|
end
|
290
398
|
end
|
291
399
|
|
@@ -304,6 +412,7 @@ class Redis
|
|
304
412
|
def format_bulk_reply(line)
|
305
413
|
bulklen = line.to_i
|
306
414
|
return if bulklen == -1
|
415
|
+
|
307
416
|
reply = encode(@sock.read(bulklen))
|
308
417
|
@sock.read(2) # Discard CRLF.
|
309
418
|
reply
|
@@ -1,14 +1,24 @@
|
|
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
|
10
18
|
include EventMachine::Deferrable
|
11
19
|
|
20
|
+
attr_accessor :timeout
|
21
|
+
|
12
22
|
def post_init
|
13
23
|
@req = nil
|
14
24
|
@connected = false
|
@@ -44,6 +54,7 @@ class Redis
|
|
44
54
|
|
45
55
|
def read
|
46
56
|
@req = EventMachine::DefaultDeferrable.new
|
57
|
+
@req.timeout(@timeout, :timeout) if @timeout > 0
|
47
58
|
EventMachine::Synchrony.sync @req
|
48
59
|
end
|
49
60
|
|
@@ -67,10 +78,20 @@ class Redis
|
|
67
78
|
|
68
79
|
def self.connect(config)
|
69
80
|
if config[:scheme] == "unix"
|
70
|
-
|
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
|
90
|
+
elsif config[:scheme] == "rediss" || config[:ssl]
|
91
|
+
raise NotImplementedError, "SSL not supported by synchrony driver"
|
71
92
|
else
|
72
93
|
conn = EventMachine.connect(config[:host], config[:port], RedisClient) do |c|
|
73
|
-
c.pending_connect_timeout = [config[:
|
94
|
+
c.pending_connect_timeout = [config[:connect_timeout], 0.1].max
|
74
95
|
end
|
75
96
|
end
|
76
97
|
|
@@ -81,7 +102,7 @@ class Redis
|
|
81
102
|
raise Errno::ECONNREFUSED if Fiber.yield == :refused
|
82
103
|
|
83
104
|
instance = new(conn)
|
84
|
-
instance.timeout = config[:
|
105
|
+
instance.timeout = config[:read_timeout]
|
85
106
|
instance
|
86
107
|
end
|
87
108
|
|
@@ -90,11 +111,11 @@ class Redis
|
|
90
111
|
end
|
91
112
|
|
92
113
|
def connected?
|
93
|
-
@connection
|
114
|
+
@connection&.connected?
|
94
115
|
end
|
95
116
|
|
96
117
|
def timeout=(timeout)
|
97
|
-
@timeout = timeout
|
118
|
+
@connection.timeout = timeout
|
98
119
|
end
|
99
120
|
|
100
121
|
def disconnect
|
@@ -109,10 +130,13 @@ class Redis
|
|
109
130
|
def read
|
110
131
|
type, payload = @connection.read
|
111
132
|
|
112
|
-
|
133
|
+
case type
|
134
|
+
when :reply
|
113
135
|
payload
|
114
|
-
|
136
|
+
when :error
|
115
137
|
raise payload
|
138
|
+
when :timeout
|
139
|
+
raise TimeoutError
|
116
140
|
else
|
117
141
|
raise "Unknown type #{type.inspect}"
|
118
142
|
end
|
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?
|