redis 3.0.0 → 4.2.2
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 +7 -0
- data/CHANGELOG.md +269 -0
- data/README.md +295 -58
- data/lib/redis.rb +1760 -451
- data/lib/redis/client.rb +355 -88
- data/lib/redis/cluster.rb +295 -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 +107 -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 +90 -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 +12 -8
- data/lib/redis/connection/registry.rb +2 -1
- data/lib/redis/connection/ruby.rb +232 -63
- data/lib/redis/connection/synchrony.rb +41 -14
- data/lib/redis/distributed.rb +205 -70
- data/lib/redis/errors.rb +48 -0
- data/lib/redis/hash_ring.rb +31 -73
- data/lib/redis/pipeline.rb +74 -18
- data/lib/redis/subscribe.rb +24 -13
- data/lib/redis/version.rb +3 -1
- metadata +63 -160
- data/.gitignore +0 -10
- data/.order +0 -169
- data/.travis.yml +0 -50
- data/.travis/Gemfile +0 -11
- data/.yardopts +0 -3
- data/Rakefile +0 -392
- data/benchmarking/logging.rb +0 -62
- 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/dist_redis.rb +0 -43
- data/examples/incr-decr.rb +0 -17
- data/examples/list.rb +0 -26
- data/examples/pubsub.rb +0 -31
- data/examples/sets.rb +0 -36
- data/examples/unicorn/config.ru +0 -3
- data/examples/unicorn/unicorn.rb +0 -20
- data/redis.gemspec +0 -41
- 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_lists_test.rb +0 -20
- data/test/commands_on_sets_test.rb +0 -77
- data/test/commands_on_sorted_sets_test.rb +0 -109
- data/test/commands_on_strings_test.rb +0 -83
- data/test/commands_on_value_types_test.rb +0 -99
- data/test/connection_handling_test.rb +0 -189
- data/test/db/.gitignore +0 -1
- data/test/distributed_blocking_commands_test.rb +0 -46
- data/test/distributed_commands_on_hashes_test.rb +0 -10
- 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 -48
- data/test/distributed_commands_on_value_types_test.rb +0 -87
- data/test/distributed_commands_requiring_clustering_test.rb +0 -148
- data/test/distributed_connection_handling_test.rb +0 -23
- data/test/distributed_internals_test.rb +0 -15
- 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 -53
- 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/helper.rb +0 -188
- data/test/helper_test.rb +0 -22
- data/test/internals_test.rb +0 -214
- data/test/lint/blocking_commands.rb +0 -124
- data/test/lint/hashes.rb +0 -162
- data/test/lint/lists.rb +0 -143
- data/test/lint/sets.rb +0 -96
- data/test/lint/sorted_sets.rb +0 -201
- data/test/lint/strings.rb +0 -157
- data/test/lint/value_types.rb +0 -106
- data/test/persistence_control_commands_test.rb +0 -26
- data/test/pipelining_commands_test.rb +0 -195
- data/test/publish_subscribe_test.rb +0 -153
- data/test/remote_server_control_commands_test.rb +0 -104
- data/test/scripting_test.rb +0 -78
- data/test/sorting_test.rb +0 -45
- 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 -92
- data/test/support/wire/synchrony.rb +0 -24
- data/test/support/wire/thread.rb +0 -5
- data/test/synchrony_driver.rb +0 -57
- data/test/test.conf +0 -9
- data/test/thread_safety_test.rb +0 -32
- data/test/transactions_test.rb +0 -244
- data/test/unknown_commands_test.rb +0 -14
- data/test/url_param_test.rb +0 -64
@@ -1,23 +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
|
13
|
+
connect_timeout = (config.fetch(:connect_timeout, 0) * 1_000_000).to_i
|
12
14
|
|
13
15
|
if config[:scheme] == "unix"
|
14
|
-
connection.connect_unix(config[:path],
|
16
|
+
connection.connect_unix(config[:path], connect_timeout)
|
17
|
+
elsif config[:scheme] == "rediss" || config[:ssl]
|
18
|
+
raise NotImplementedError, "SSL not supported by hiredis driver"
|
15
19
|
else
|
16
|
-
connection.connect(config[:host], config[:port],
|
20
|
+
connection.connect(config[:host], config[:port], connect_timeout)
|
17
21
|
end
|
18
22
|
|
19
23
|
instance = new(connection)
|
20
|
-
instance.timeout = config[:
|
24
|
+
instance.timeout = config[:read_timeout]
|
21
25
|
instance
|
22
26
|
rescue Errno::ETIMEDOUT
|
23
27
|
raise TimeoutError
|
@@ -28,7 +32,7 @@ class Redis
|
|
28
32
|
end
|
29
33
|
|
30
34
|
def connected?
|
31
|
-
@connection
|
35
|
+
@connection&.connected?
|
32
36
|
end
|
33
37
|
|
34
38
|
def timeout=(timeout)
|
@@ -54,7 +58,7 @@ class Redis
|
|
54
58
|
rescue Errno::EAGAIN
|
55
59
|
raise TimeoutError
|
56
60
|
rescue RuntimeError => err
|
57
|
-
raise ProtocolError
|
61
|
+
raise ProtocolError, err.message
|
58
62
|
end
|
59
63
|
end
|
60
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,44 +1,48 @@
|
|
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)
|
@@ -47,18 +51,57 @@ class Redis
|
|
47
51
|
def _read_from_socket(nbytes)
|
48
52
|
begin
|
49
53
|
read_nonblock(nbytes)
|
50
|
-
|
51
|
-
rescue Errno::EWOULDBLOCK, Errno::EAGAIN
|
54
|
+
rescue IO::WaitReadable
|
52
55
|
if IO.select([self], nil, nil, @timeout)
|
53
56
|
retry
|
54
57
|
else
|
55
58
|
raise Redis::TimeoutError
|
56
59
|
end
|
60
|
+
rescue IO::WaitWritable
|
61
|
+
if IO.select(nil, [self], nil, @timeout)
|
62
|
+
retry
|
63
|
+
else
|
64
|
+
raise Redis::TimeoutError
|
65
|
+
end
|
57
66
|
end
|
67
|
+
rescue EOFError
|
68
|
+
raise Errno::ECONNRESET
|
69
|
+
end
|
58
70
|
|
71
|
+
def _write_to_socket(data)
|
72
|
+
begin
|
73
|
+
write_nonblock(data)
|
74
|
+
rescue IO::WaitWritable
|
75
|
+
if IO.select(nil, [self], nil, @write_timeout)
|
76
|
+
retry
|
77
|
+
else
|
78
|
+
raise Redis::TimeoutError
|
79
|
+
end
|
80
|
+
rescue IO::WaitReadable
|
81
|
+
if IO.select([self], nil, nil, @write_timeout)
|
82
|
+
retry
|
83
|
+
else
|
84
|
+
raise Redis::TimeoutError
|
85
|
+
end
|
86
|
+
end
|
59
87
|
rescue EOFError
|
60
88
|
raise Errno::ECONNRESET
|
61
89
|
end
|
90
|
+
|
91
|
+
def write(data)
|
92
|
+
return super(data) unless @write_timeout
|
93
|
+
|
94
|
+
length = data.bytesize
|
95
|
+
total_count = 0
|
96
|
+
loop do
|
97
|
+
count = _write_to_socket(data)
|
98
|
+
|
99
|
+
total_count += count
|
100
|
+
return total_count if total_count >= length
|
101
|
+
|
102
|
+
data = data.byteslice(count..-1)
|
103
|
+
end
|
104
|
+
end
|
62
105
|
end
|
63
106
|
|
64
107
|
if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
|
@@ -66,7 +109,6 @@ class Redis
|
|
66
109
|
require "timeout"
|
67
110
|
|
68
111
|
class TCPSocket < ::TCPSocket
|
69
|
-
|
70
112
|
include SocketMixin
|
71
113
|
|
72
114
|
def self.connect(host, port, timeout)
|
@@ -79,42 +121,46 @@ class Redis
|
|
79
121
|
end
|
80
122
|
end
|
81
123
|
|
82
|
-
|
124
|
+
if defined?(::UNIXSocket)
|
83
125
|
|
84
|
-
|
85
|
-
|
86
|
-
# readable. This behavior shows in 1.6.6 in both 1.8 and 1.9 mode.
|
87
|
-
# Therefore, fall back on the default Unix socket implementation,
|
88
|
-
# without timeouts.
|
126
|
+
class UNIXSocket < ::UNIXSocket
|
127
|
+
include SocketMixin
|
89
128
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
129
|
+
def self.connect(path, timeout)
|
130
|
+
Timeout.timeout(timeout) do
|
131
|
+
sock = new(path)
|
132
|
+
sock
|
133
|
+
end
|
134
|
+
rescue Timeout::Error
|
135
|
+
raise TimeoutError
|
136
|
+
end
|
137
|
+
|
138
|
+
# JRuby raises Errno::EAGAIN on #read_nonblock even when IO.select
|
139
|
+
# says it is readable (1.6.6, in both 1.8 and 1.9 mode).
|
140
|
+
# Use the blocking #readpartial method instead.
|
141
|
+
|
142
|
+
def _read_from_socket(nbytes)
|
143
|
+
readpartial(nbytes)
|
144
|
+
rescue EOFError
|
145
|
+
raise Errno::ECONNRESET
|
94
146
|
end
|
95
|
-
rescue Timeout::Error
|
96
|
-
raise TimeoutError
|
97
147
|
end
|
148
|
+
|
98
149
|
end
|
99
150
|
|
100
151
|
else
|
101
152
|
|
102
153
|
class TCPSocket < ::Socket
|
103
|
-
|
104
154
|
include SocketMixin
|
105
155
|
|
106
|
-
def self.
|
107
|
-
|
108
|
-
|
109
|
-
sock = new(::Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0)
|
110
|
-
sockaddr = ::Socket.pack_sockaddr_in(port, addr[0][3])
|
156
|
+
def self.connect_addrinfo(addrinfo, port, timeout)
|
157
|
+
sock = new(::Socket.const_get(addrinfo[0]), Socket::SOCK_STREAM, 0)
|
158
|
+
sockaddr = ::Socket.pack_sockaddr_in(port, addrinfo[3])
|
111
159
|
|
112
160
|
begin
|
113
161
|
sock.connect_nonblock(sockaddr)
|
114
162
|
rescue Errno::EINPROGRESS
|
115
|
-
if IO.select(nil, [sock], nil, timeout)
|
116
|
-
raise TimeoutError
|
117
|
-
end
|
163
|
+
raise TimeoutError if IO.select(nil, [sock], nil, timeout).nil?
|
118
164
|
|
119
165
|
begin
|
120
166
|
sock.connect_nonblock(sockaddr)
|
@@ -124,12 +170,43 @@ class Redis
|
|
124
170
|
|
125
171
|
sock
|
126
172
|
end
|
173
|
+
|
174
|
+
def self.connect(host, port, timeout)
|
175
|
+
# Don't pass AI_ADDRCONFIG as flag to getaddrinfo(3)
|
176
|
+
#
|
177
|
+
# From the man page for getaddrinfo(3):
|
178
|
+
#
|
179
|
+
# If hints.ai_flags includes the AI_ADDRCONFIG flag, then IPv4
|
180
|
+
# addresses are returned in the list pointed to by res only if the
|
181
|
+
# local system has at least one IPv4 address configured, and IPv6
|
182
|
+
# addresses are returned only if the local system has at least one
|
183
|
+
# IPv6 address configured. The loopback address is not considered
|
184
|
+
# for this case as valid as a configured address.
|
185
|
+
#
|
186
|
+
# We do want the IPv6 loopback address to be returned if applicable,
|
187
|
+
# even if it is the only configured IPv6 address on the machine.
|
188
|
+
# Also see: https://github.com/redis/redis-rb/pull/394.
|
189
|
+
addrinfo = ::Socket.getaddrinfo(host, nil, Socket::AF_UNSPEC, Socket::SOCK_STREAM)
|
190
|
+
|
191
|
+
# From the man page for getaddrinfo(3):
|
192
|
+
#
|
193
|
+
# Normally, the application should try using the addresses in the
|
194
|
+
# order in which they are returned. The sorting function used
|
195
|
+
# within getaddrinfo() is defined in RFC 3484 [...].
|
196
|
+
#
|
197
|
+
addrinfo.each_with_index do |ai, i|
|
198
|
+
begin
|
199
|
+
return connect_addrinfo(ai, port, timeout)
|
200
|
+
rescue SystemCallError
|
201
|
+
# Raise if this was our last attempt.
|
202
|
+
raise if addrinfo.length == i + 1
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
127
206
|
end
|
128
207
|
|
129
208
|
class UNIXSocket < ::Socket
|
130
|
-
|
131
|
-
# This class doesn't include the mixin to keep its behavior in sync
|
132
|
-
# with the JRuby implementation.
|
209
|
+
include SocketMixin
|
133
210
|
|
134
211
|
def self.connect(path, timeout)
|
135
212
|
sock = new(::Socket::AF_UNIX, Socket::SOCK_STREAM, 0)
|
@@ -138,9 +215,7 @@ class Redis
|
|
138
215
|
begin
|
139
216
|
sock.connect_nonblock(sockaddr)
|
140
217
|
rescue Errno::EINPROGRESS
|
141
|
-
if IO.select(nil, [sock], nil, timeout)
|
142
|
-
raise TimeoutError
|
143
|
-
end
|
218
|
+
raise TimeoutError if IO.select(nil, [sock], nil, timeout).nil?
|
144
219
|
|
145
220
|
begin
|
146
221
|
sock.connect_nonblock(sockaddr)
|
@@ -154,33 +229,125 @@ class Redis
|
|
154
229
|
|
155
230
|
end
|
156
231
|
|
232
|
+
if defined?(OpenSSL)
|
233
|
+
class SSLSocket < ::OpenSSL::SSL::SSLSocket
|
234
|
+
include SocketMixin
|
235
|
+
|
236
|
+
def self.connect(host, port, timeout, ssl_params)
|
237
|
+
# Note: this is using Redis::Connection::TCPSocket
|
238
|
+
tcp_sock = TCPSocket.connect(host, port, timeout)
|
239
|
+
|
240
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
241
|
+
|
242
|
+
# The provided parameters are merged into OpenSSL::SSL::SSLContext::DEFAULT_PARAMS
|
243
|
+
ctx.set_params(ssl_params || {})
|
244
|
+
|
245
|
+
ssl_sock = new(tcp_sock, ctx)
|
246
|
+
ssl_sock.hostname = host
|
247
|
+
|
248
|
+
begin
|
249
|
+
# Initiate the socket connection in the background. If it doesn't fail
|
250
|
+
# immediately it will raise an IO::WaitWritable (Errno::EINPROGRESS)
|
251
|
+
# indicating the connection is in progress.
|
252
|
+
# Unlike waiting for a tcp socket to connect, you can't time out ssl socket
|
253
|
+
# connections during the connect phase properly, because IO.select only partially works.
|
254
|
+
# Instead, you have to retry.
|
255
|
+
ssl_sock.connect_nonblock
|
256
|
+
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable
|
257
|
+
if IO.select([ssl_sock], nil, nil, timeout)
|
258
|
+
retry
|
259
|
+
else
|
260
|
+
raise TimeoutError
|
261
|
+
end
|
262
|
+
rescue IO::WaitWritable
|
263
|
+
if IO.select(nil, [ssl_sock], nil, timeout)
|
264
|
+
retry
|
265
|
+
else
|
266
|
+
raise TimeoutError
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
unless ctx.verify_mode == OpenSSL::SSL::VERIFY_NONE || (
|
271
|
+
ctx.respond_to?(:verify_hostname) &&
|
272
|
+
!ctx.verify_hostname
|
273
|
+
)
|
274
|
+
ssl_sock.post_connection_check(host)
|
275
|
+
end
|
276
|
+
|
277
|
+
ssl_sock
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
157
282
|
class Ruby
|
158
283
|
include Redis::Connection::CommandHelper
|
159
284
|
|
160
|
-
MINUS = "-"
|
161
|
-
PLUS = "+"
|
162
|
-
COLON = ":"
|
163
|
-
DOLLAR = "$"
|
164
|
-
ASTERISK = "*"
|
285
|
+
MINUS = "-"
|
286
|
+
PLUS = "+"
|
287
|
+
COLON = ":"
|
288
|
+
DOLLAR = "$"
|
289
|
+
ASTERISK = "*"
|
165
290
|
|
166
291
|
def self.connect(config)
|
167
292
|
if config[:scheme] == "unix"
|
168
|
-
|
293
|
+
raise ArgumentError, "SSL incompatible with unix sockets" if config[:ssl]
|
294
|
+
|
295
|
+
sock = UNIXSocket.connect(config[:path], config[:connect_timeout])
|
296
|
+
elsif config[:scheme] == "rediss" || config[:ssl]
|
297
|
+
sock = SSLSocket.connect(config[:host], config[:port], config[:connect_timeout], config[:ssl_params])
|
169
298
|
else
|
170
|
-
sock = TCPSocket.connect(config[:host], config[:port], config[:
|
299
|
+
sock = TCPSocket.connect(config[:host], config[:port], config[:connect_timeout])
|
171
300
|
end
|
172
301
|
|
173
302
|
instance = new(sock)
|
174
|
-
instance.timeout = config[:
|
303
|
+
instance.timeout = config[:read_timeout]
|
304
|
+
instance.write_timeout = config[:write_timeout]
|
305
|
+
instance.set_tcp_keepalive config[:tcp_keepalive]
|
306
|
+
instance.set_tcp_nodelay if sock.is_a? TCPSocket
|
175
307
|
instance
|
176
308
|
end
|
177
309
|
|
310
|
+
if %i[SOL_SOCKET SO_KEEPALIVE SOL_TCP TCP_KEEPIDLE TCP_KEEPINTVL TCP_KEEPCNT].all? { |c| Socket.const_defined? c }
|
311
|
+
def set_tcp_keepalive(keepalive)
|
312
|
+
return unless keepalive.is_a?(Hash)
|
313
|
+
|
314
|
+
@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
|
315
|
+
@sock.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE, keepalive[:time])
|
316
|
+
@sock.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL, keepalive[:intvl])
|
317
|
+
@sock.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT, keepalive[:probes])
|
318
|
+
end
|
319
|
+
|
320
|
+
def get_tcp_keepalive
|
321
|
+
{
|
322
|
+
time: @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE).int,
|
323
|
+
intvl: @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL).int,
|
324
|
+
probes: @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT).int
|
325
|
+
}
|
326
|
+
end
|
327
|
+
else
|
328
|
+
def set_tcp_keepalive(keepalive); end
|
329
|
+
|
330
|
+
def get_tcp_keepalive
|
331
|
+
{
|
332
|
+
}
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
# disables Nagle's Algorithm, prevents multiple round trips with MULTI
|
337
|
+
if %i[IPPROTO_TCP TCP_NODELAY].all? { |c| Socket.const_defined? c }
|
338
|
+
def set_tcp_nodelay
|
339
|
+
@sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
340
|
+
end
|
341
|
+
else
|
342
|
+
def set_tcp_nodelay; end
|
343
|
+
end
|
344
|
+
|
178
345
|
def initialize(sock)
|
179
346
|
@sock = sock
|
180
347
|
end
|
181
348
|
|
182
349
|
def connected?
|
183
|
-
|
350
|
+
!!@sock
|
184
351
|
end
|
185
352
|
|
186
353
|
def disconnect
|
@@ -191,9 +358,11 @@ class Redis
|
|
191
358
|
end
|
192
359
|
|
193
360
|
def timeout=(timeout)
|
194
|
-
if @sock.respond_to?(:timeout=)
|
195
|
-
|
196
|
-
|
361
|
+
@sock.timeout = timeout if @sock.respond_to?(:timeout=)
|
362
|
+
end
|
363
|
+
|
364
|
+
def write_timeout=(timeout)
|
365
|
+
@sock.write_timeout = timeout
|
197
366
|
end
|
198
367
|
|
199
368
|
def write(command)
|
@@ -204,7 +373,6 @@ class Redis
|
|
204
373
|
line = @sock.gets
|
205
374
|
reply_type = line.slice!(0, 1)
|
206
375
|
format_reply(reply_type, line)
|
207
|
-
|
208
376
|
rescue Errno::EAGAIN
|
209
377
|
raise TimeoutError
|
210
378
|
end
|
@@ -216,7 +384,7 @@ class Redis
|
|
216
384
|
when COLON then format_integer_reply(line)
|
217
385
|
when DOLLAR then format_bulk_reply(line)
|
218
386
|
when ASTERISK then format_multi_bulk_reply(line)
|
219
|
-
else raise ProtocolError
|
387
|
+
else raise ProtocolError, reply_type
|
220
388
|
end
|
221
389
|
end
|
222
390
|
|
@@ -235,6 +403,7 @@ class Redis
|
|
235
403
|
def format_bulk_reply(line)
|
236
404
|
bulklen = line.to_i
|
237
405
|
return if bulklen == -1
|
406
|
+
|
238
407
|
reply = encode(@sock.read(bulklen))
|
239
408
|
@sock.read(2) # Discard CRLF.
|
240
409
|
reply
|