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