redis 4.1.4 → 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 +4 -4
- data/CHANGELOG.md +77 -0
- data/README.md +27 -17
- data/lib/redis/client.rb +108 -74
- data/lib/redis/cluster/command_loader.rb +6 -7
- data/lib/redis/cluster/node.rb +5 -1
- data/lib/redis/cluster/option.rb +9 -3
- data/lib/redis/cluster/slot.rb +28 -14
- data/lib/redis/cluster/slot_loader.rb +2 -3
- data/lib/redis/cluster.rb +13 -13
- data/lib/redis/connection/command_helper.rb +4 -2
- data/lib/redis/connection/hiredis.rb +3 -3
- data/lib/redis/connection/registry.rb +1 -1
- data/lib/redis/connection/ruby.rb +92 -109
- data/lib/redis/connection/synchrony.rb +8 -4
- data/lib/redis/connection.rb +1 -0
- data/lib/redis/distributed.rb +161 -63
- data/lib/redis/errors.rb +1 -0
- data/lib/redis/hash_ring.rb +14 -14
- data/lib/redis/pipeline.rb +6 -8
- data/lib/redis/subscribe.rb +10 -12
- data/lib/redis/version.rb +2 -1
- data/lib/redis.rb +597 -261
- metadata +15 -10
data/lib/redis/cluster/slot.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'set'
|
4
|
-
|
5
3
|
class Redis
|
6
4
|
class Cluster
|
7
5
|
# Keep slot and node key map for Redis Cluster Client
|
@@ -28,11 +26,20 @@ class Redis
|
|
28
26
|
return nil unless exists?(slot)
|
29
27
|
return find_node_key_of_master(slot) if replica_disabled?
|
30
28
|
|
31
|
-
@map[slot][:slaves].
|
29
|
+
@map[slot][:slaves].sample
|
32
30
|
end
|
33
31
|
|
34
32
|
def put(slot, node_key)
|
35
|
-
|
33
|
+
# Since we're sharing a hash for build_slot_node_key_map, duplicate it
|
34
|
+
# if it already exists instead of preserving as-is.
|
35
|
+
@map[slot] = @map[slot] ? @map[slot].dup : { master: nil, slaves: [] }
|
36
|
+
|
37
|
+
if master?(node_key)
|
38
|
+
@map[slot][:master] = node_key
|
39
|
+
elsif !@map[slot][:slaves].include?(node_key)
|
40
|
+
@map[slot][:slaves] << node_key
|
41
|
+
end
|
42
|
+
|
36
43
|
nil
|
37
44
|
end
|
38
45
|
|
@@ -52,20 +59,27 @@ class Redis
|
|
52
59
|
|
53
60
|
# available_slots is mapping of node_key to list of slot ranges
|
54
61
|
def build_slot_node_key_map(available_slots)
|
55
|
-
|
56
|
-
|
57
|
-
|
62
|
+
by_ranges = {}
|
63
|
+
available_slots.each do |node_key, slots_arr|
|
64
|
+
by_ranges[slots_arr] ||= { master: nil, slaves: [] }
|
65
|
+
|
66
|
+
if master?(node_key)
|
67
|
+
by_ranges[slots_arr][:master] = node_key
|
68
|
+
elsif !by_ranges[slots_arr][:slaves].include?(node_key)
|
69
|
+
by_ranges[slots_arr][:slaves] << node_key
|
58
70
|
end
|
59
71
|
end
|
60
|
-
end
|
61
72
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
73
|
+
by_slot = {}
|
74
|
+
by_ranges.each do |slots_arr, nodes|
|
75
|
+
slots_arr.each do |slots|
|
76
|
+
slots.each do |slot|
|
77
|
+
by_slot[slot] = nodes
|
78
|
+
end
|
79
|
+
end
|
68
80
|
end
|
81
|
+
|
82
|
+
by_slot
|
69
83
|
end
|
70
84
|
end
|
71
85
|
end
|
@@ -25,9 +25,8 @@ class Redis
|
|
25
25
|
def fetch_slot_info(node)
|
26
26
|
hash_with_default_arr = Hash.new { |h, k| h[k] = [] }
|
27
27
|
node.call(%i[cluster slots])
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
.flat_map { |arr| parse_slot_info(arr, default_ip: node.host) }
|
29
|
+
.each_with_object(hash_with_default_arr) { |arr, h| h[arr[0]] << arr[1] }
|
31
30
|
rescue CannotConnectError, ConnectionError, CommandError
|
32
31
|
{} # can retry on another node
|
33
32
|
end
|
data/lib/redis/cluster.rb
CHANGED
@@ -78,10 +78,13 @@ class Redis
|
|
78
78
|
end
|
79
79
|
|
80
80
|
def call_pipeline(pipeline)
|
81
|
-
node_keys
|
82
|
-
|
83
|
-
|
84
|
-
|
81
|
+
node_keys = pipeline.commands.map { |cmd| find_node_key(cmd, primary_only: true) }.compact.uniq
|
82
|
+
if node_keys.size > 1
|
83
|
+
raise(CrossSlotPipeliningError,
|
84
|
+
pipeline.commands.map { |cmd| @command.extract_first_key(cmd) }.reject(&:empty?).uniq)
|
85
|
+
end
|
86
|
+
|
87
|
+
try_send(find_node(node_keys.first), :call_pipeline, pipeline)
|
85
88
|
end
|
86
89
|
|
87
90
|
def call_with_timeout(command, timeout, &block)
|
@@ -127,7 +130,7 @@ class Redis
|
|
127
130
|
def send_command(command, &block)
|
128
131
|
cmd = command.first.to_s.downcase
|
129
132
|
case cmd
|
130
|
-
when 'auth', 'bgrewriteaof', 'bgsave', 'quit', 'save'
|
133
|
+
when 'acl', 'auth', 'bgrewriteaof', 'bgsave', 'quit', 'save'
|
131
134
|
@node.call_all(command, &block).first
|
132
135
|
when 'flushall', 'flushdb'
|
133
136
|
@node.call_master(command, &block).first
|
@@ -216,11 +219,13 @@ class Redis
|
|
216
219
|
rescue CommandError => err
|
217
220
|
if err.message.start_with?('MOVED')
|
218
221
|
raise if retry_count <= 0
|
222
|
+
|
219
223
|
node = assign_redirection_node(err.message)
|
220
224
|
retry_count -= 1
|
221
225
|
retry
|
222
226
|
elsif err.message.start_with?('ASK')
|
223
227
|
raise if retry_count <= 0
|
228
|
+
|
224
229
|
node = assign_asking_node(err.message)
|
225
230
|
node.call(%i[asking])
|
226
231
|
retry_count -= 1
|
@@ -250,14 +255,14 @@ class Redis
|
|
250
255
|
find_node(node_key)
|
251
256
|
end
|
252
257
|
|
253
|
-
def find_node_key(command)
|
258
|
+
def find_node_key(command, primary_only: false)
|
254
259
|
key = @command.extract_first_key(command)
|
255
260
|
return if key.empty?
|
256
261
|
|
257
262
|
slot = KeySlotConverter.convert(key)
|
258
263
|
return unless @slot.exists?(slot)
|
259
264
|
|
260
|
-
if @command.should_send_to_master?(command)
|
265
|
+
if @command.should_send_to_master?(command) || primary_only
|
261
266
|
@slot.find_node_key_of_master(slot)
|
262
267
|
else
|
263
268
|
@slot.find_node_key_of_slave(slot)
|
@@ -266,6 +271,7 @@ class Redis
|
|
266
271
|
|
267
272
|
def find_node(node_key)
|
268
273
|
return @node.sample if node_key.nil?
|
274
|
+
|
269
275
|
@node.find_by(node_key)
|
270
276
|
rescue Node::ReloadNeeded
|
271
277
|
update_cluster_info!(node_key)
|
@@ -281,11 +287,5 @@ class Redis
|
|
281
287
|
@node.map(&:disconnect)
|
282
288
|
@node, @slot = fetch_cluster_info!(@option)
|
283
289
|
end
|
284
|
-
|
285
|
-
def extract_keys_in_pipeline(pipeline)
|
286
|
-
node_keys = pipeline.commands.map { |cmd| find_node_key(cmd) }.compact.uniq
|
287
|
-
command_keys = pipeline.commands.map { |cmd| @command.extract_first_key(cmd) }.reject(&:empty?)
|
288
|
-
[node_keys, command_keys]
|
289
|
-
end
|
290
290
|
end
|
291
291
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
class Redis
|
3
4
|
module Connection
|
4
5
|
module CommandHelper
|
5
|
-
|
6
6
|
COMMAND_DELIMITER = "\r\n"
|
7
7
|
|
8
8
|
def build_command(args)
|
@@ -12,11 +12,13 @@ class Redis
|
|
12
12
|
if i.is_a? Array
|
13
13
|
i.each do |j|
|
14
14
|
j = j.to_s
|
15
|
+
j = j.encoding == Encoding::BINARY ? j : j.b
|
15
16
|
command << "$#{j.bytesize}"
|
16
17
|
command << j
|
17
18
|
end
|
18
19
|
else
|
19
20
|
i = i.to_s
|
21
|
+
i = i.encoding == Encoding::BINARY ? i : i.b
|
20
22
|
command << "$#{i.bytesize}"
|
21
23
|
command << i
|
22
24
|
end
|
@@ -29,7 +31,7 @@ class Redis
|
|
29
31
|
command.join(COMMAND_DELIMITER)
|
30
32
|
end
|
31
33
|
|
32
|
-
|
34
|
+
protected
|
33
35
|
|
34
36
|
def encode(string)
|
35
37
|
string.force_encoding(Encoding.default_external)
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require_relative "registry"
|
3
4
|
require_relative "../errors"
|
4
5
|
require "hiredis/connection"
|
@@ -7,7 +8,6 @@ require "timeout"
|
|
7
8
|
class Redis
|
8
9
|
module Connection
|
9
10
|
class Hiredis
|
10
|
-
|
11
11
|
def self.connect(config)
|
12
12
|
connection = ::Hiredis::Connection.new
|
13
13
|
connect_timeout = (config.fetch(:connect_timeout, 0) * 1_000_000).to_i
|
@@ -32,7 +32,7 @@ class Redis
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def connected?
|
35
|
-
@connection
|
35
|
+
@connection&.connected?
|
36
36
|
end
|
37
37
|
|
38
38
|
def timeout=(timeout)
|
@@ -58,7 +58,7 @@ class Redis
|
|
58
58
|
rescue Errno::EAGAIN
|
59
59
|
raise TimeoutError
|
60
60
|
rescue RuntimeError => err
|
61
|
-
raise ProtocolError
|
61
|
+
raise ProtocolError, err.message
|
62
62
|
end
|
63
63
|
end
|
64
64
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
class Redis
|
3
4
|
module Connection
|
4
|
-
|
5
5
|
# Store a list of loaded connection drivers in the Connection module.
|
6
6
|
# Redis::Client uses the last required driver by default, and will be aware
|
7
7
|
# of the loaded connection drivers if the user chooses to override the
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require_relative "registry"
|
3
4
|
require_relative "command_helper"
|
4
5
|
require_relative "../errors"
|
@@ -14,108 +15,85 @@ end
|
|
14
15
|
class Redis
|
15
16
|
module Connection
|
16
17
|
module SocketMixin
|
17
|
-
|
18
|
-
CRLF = "\r\n".freeze
|
18
|
+
CRLF = "\r\n"
|
19
19
|
|
20
20
|
def initialize(*args)
|
21
21
|
super(*args)
|
22
22
|
|
23
23
|
@timeout = @write_timeout = nil
|
24
|
-
@buffer = "".
|
24
|
+
@buffer = "".b
|
25
25
|
end
|
26
26
|
|
27
27
|
def timeout=(timeout)
|
28
|
-
if timeout && timeout > 0
|
29
|
-
@timeout = timeout
|
30
|
-
else
|
31
|
-
@timeout = nil
|
32
|
-
end
|
28
|
+
@timeout = (timeout if timeout && timeout > 0)
|
33
29
|
end
|
34
30
|
|
35
31
|
def write_timeout=(timeout)
|
36
|
-
if timeout && timeout > 0
|
37
|
-
@write_timeout = timeout
|
38
|
-
else
|
39
|
-
@write_timeout = nil
|
40
|
-
end
|
32
|
+
@write_timeout = (timeout if timeout && timeout > 0)
|
41
33
|
end
|
42
34
|
|
43
35
|
def read(nbytes)
|
44
36
|
result = @buffer.slice!(0, nbytes)
|
45
37
|
|
46
|
-
|
47
|
-
|
48
|
-
end
|
38
|
+
buffer = String.new(capacity: nbytes, encoding: Encoding::ASCII_8BIT)
|
39
|
+
result << _read_from_socket(nbytes - result.bytesize, buffer) while result.bytesize < nbytes
|
49
40
|
|
50
41
|
result
|
51
42
|
end
|
52
43
|
|
53
44
|
def gets
|
54
|
-
crlf = nil
|
55
|
-
|
56
|
-
while (crlf = @buffer.index(CRLF)) == nil
|
57
|
-
@buffer << _read_from_socket(16384)
|
45
|
+
while (crlf = @buffer.index(CRLF)).nil?
|
46
|
+
@buffer << _read_from_socket(16_384)
|
58
47
|
end
|
59
48
|
|
60
49
|
@buffer.slice!(0, crlf + CRLF.bytesize)
|
61
50
|
end
|
62
51
|
|
63
|
-
def _read_from_socket(nbytes)
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
raise Redis::TimeoutError
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
rescue EOFError
|
83
|
-
raise Errno::ECONNRESET
|
84
|
-
end
|
85
|
-
|
86
|
-
def _write_to_socket(data)
|
87
|
-
begin
|
88
|
-
write_nonblock(data)
|
89
|
-
|
90
|
-
rescue IO::WaitWritable
|
91
|
-
if IO.select(nil, [self], nil, @write_timeout)
|
92
|
-
retry
|
93
|
-
else
|
94
|
-
raise Redis::TimeoutError
|
95
|
-
end
|
96
|
-
rescue IO::WaitReadable
|
97
|
-
if IO.select([self], nil, nil, @write_timeout)
|
98
|
-
retry
|
99
|
-
else
|
100
|
-
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
|
101
67
|
end
|
102
68
|
end
|
103
|
-
|
104
|
-
rescue EOFError
|
105
|
-
raise Errno::ECONNRESET
|
106
69
|
end
|
107
70
|
|
108
|
-
def write(
|
109
|
-
return super(
|
71
|
+
def write(buffer)
|
72
|
+
return super(buffer) unless @write_timeout
|
110
73
|
|
111
|
-
|
112
|
-
|
74
|
+
bytes_to_write = buffer.bytesize
|
75
|
+
total_bytes_written = 0
|
113
76
|
loop do
|
114
|
-
|
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
|
115
90
|
|
116
|
-
|
117
|
-
|
118
|
-
|
91
|
+
if total_bytes_written >= bytes_to_write
|
92
|
+
return total_bytes_written
|
93
|
+
end
|
94
|
+
|
95
|
+
buffer = buffer.byteslice(bytes_written..-1)
|
96
|
+
end
|
119
97
|
end
|
120
98
|
end
|
121
99
|
end
|
@@ -125,7 +103,6 @@ class Redis
|
|
125
103
|
require "timeout"
|
126
104
|
|
127
105
|
class TCPSocket < ::TCPSocket
|
128
|
-
|
129
106
|
include SocketMixin
|
130
107
|
|
131
108
|
def self.connect(host, port, timeout)
|
@@ -141,7 +118,6 @@ class Redis
|
|
141
118
|
if defined?(::UNIXSocket)
|
142
119
|
|
143
120
|
class UNIXSocket < ::UNIXSocket
|
144
|
-
|
145
121
|
include SocketMixin
|
146
122
|
|
147
123
|
def self.connect(path, timeout)
|
@@ -153,13 +129,12 @@ class Redis
|
|
153
129
|
raise TimeoutError
|
154
130
|
end
|
155
131
|
|
156
|
-
# JRuby raises Errno::EAGAIN on #read_nonblock even when
|
132
|
+
# JRuby raises Errno::EAGAIN on #read_nonblock even when it
|
157
133
|
# says it is readable (1.6.6, in both 1.8 and 1.9 mode).
|
158
134
|
# Use the blocking #readpartial method instead.
|
159
135
|
|
160
136
|
def _read_from_socket(nbytes)
|
161
137
|
readpartial(nbytes)
|
162
|
-
|
163
138
|
rescue EOFError
|
164
139
|
raise Errno::ECONNRESET
|
165
140
|
end
|
@@ -170,19 +145,16 @@ class Redis
|
|
170
145
|
else
|
171
146
|
|
172
147
|
class TCPSocket < ::Socket
|
173
|
-
|
174
148
|
include SocketMixin
|
175
149
|
|
176
|
-
def self.connect_addrinfo(
|
177
|
-
sock = new(::Socket.const_get(
|
178
|
-
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])
|
179
153
|
|
180
154
|
begin
|
181
155
|
sock.connect_nonblock(sockaddr)
|
182
156
|
rescue Errno::EINPROGRESS
|
183
|
-
|
184
|
-
raise TimeoutError
|
185
|
-
end
|
157
|
+
raise TimeoutError unless sock.wait_writable(timeout)
|
186
158
|
|
187
159
|
begin
|
188
160
|
sock.connect_nonblock(sockaddr)
|
@@ -221,14 +193,13 @@ class Redis
|
|
221
193
|
return connect_addrinfo(ai, port, timeout)
|
222
194
|
rescue SystemCallError
|
223
195
|
# Raise if this was our last attempt.
|
224
|
-
raise if addrinfo.length == i+1
|
196
|
+
raise if addrinfo.length == i + 1
|
225
197
|
end
|
226
198
|
end
|
227
199
|
end
|
228
200
|
end
|
229
201
|
|
230
202
|
class UNIXSocket < ::Socket
|
231
|
-
|
232
203
|
include SocketMixin
|
233
204
|
|
234
205
|
def self.connect(path, timeout)
|
@@ -238,9 +209,7 @@ class Redis
|
|
238
209
|
begin
|
239
210
|
sock.connect_nonblock(sockaddr)
|
240
211
|
rescue Errno::EINPROGRESS
|
241
|
-
|
242
|
-
raise TimeoutError
|
243
|
-
end
|
212
|
+
raise TimeoutError unless sock.wait_writable(timeout)
|
244
213
|
|
245
214
|
begin
|
246
215
|
sock.connect_nonblock(sockaddr)
|
@@ -258,12 +227,26 @@ class Redis
|
|
258
227
|
class SSLSocket < ::OpenSSL::SSL::SSLSocket
|
259
228
|
include SocketMixin
|
260
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
|
+
|
261
242
|
def self.connect(host, port, timeout, ssl_params)
|
262
243
|
# Note: this is using Redis::Connection::TCPSocket
|
263
244
|
tcp_sock = TCPSocket.connect(host, port, timeout)
|
264
245
|
|
265
246
|
ctx = OpenSSL::SSL::SSLContext.new
|
266
|
-
|
247
|
+
|
248
|
+
# The provided parameters are merged into OpenSSL::SSL::SSLContext::DEFAULT_PARAMS
|
249
|
+
ctx.set_params(ssl_params || {})
|
267
250
|
|
268
251
|
ssl_sock = new(tcp_sock, ctx)
|
269
252
|
ssl_sock.hostname = host
|
@@ -277,20 +260,23 @@ class Redis
|
|
277
260
|
# Instead, you have to retry.
|
278
261
|
ssl_sock.connect_nonblock
|
279
262
|
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable
|
280
|
-
if
|
263
|
+
if ssl_sock.wait_readable(timeout)
|
281
264
|
retry
|
282
265
|
else
|
283
266
|
raise TimeoutError
|
284
267
|
end
|
285
268
|
rescue IO::WaitWritable
|
286
|
-
if
|
269
|
+
if ssl_sock.wait_writable(timeout)
|
287
270
|
retry
|
288
271
|
else
|
289
272
|
raise TimeoutError
|
290
273
|
end
|
291
274
|
end
|
292
275
|
|
293
|
-
unless ctx.verify_mode == OpenSSL::SSL::VERIFY_NONE || (
|
276
|
+
unless ctx.verify_mode == OpenSSL::SSL::VERIFY_NONE || (
|
277
|
+
ctx.respond_to?(:verify_hostname) &&
|
278
|
+
!ctx.verify_hostname
|
279
|
+
)
|
294
280
|
ssl_sock.post_connection_check(host)
|
295
281
|
end
|
296
282
|
|
@@ -302,15 +288,16 @@ class Redis
|
|
302
288
|
class Ruby
|
303
289
|
include Redis::Connection::CommandHelper
|
304
290
|
|
305
|
-
MINUS = "-"
|
306
|
-
PLUS = "+"
|
307
|
-
COLON = ":"
|
308
|
-
DOLLAR = "$"
|
309
|
-
ASTERISK = "*"
|
291
|
+
MINUS = "-"
|
292
|
+
PLUS = "+"
|
293
|
+
COLON = ":"
|
294
|
+
DOLLAR = "$"
|
295
|
+
ASTERISK = "*"
|
310
296
|
|
311
297
|
def self.connect(config)
|
312
298
|
if config[:scheme] == "unix"
|
313
299
|
raise ArgumentError, "SSL incompatible with unix sockets" if config[:ssl]
|
300
|
+
|
314
301
|
sock = UNIXSocket.connect(config[:path], config[:connect_timeout])
|
315
302
|
elsif config[:scheme] == "rediss" || config[:ssl]
|
316
303
|
sock = SSLSocket.connect(config[:host], config[:port], config[:connect_timeout], config[:ssl_params])
|
@@ -326,7 +313,7 @@ class Redis
|
|
326
313
|
instance
|
327
314
|
end
|
328
315
|
|
329
|
-
if [
|
316
|
+
if %i[SOL_SOCKET SO_KEEPALIVE SOL_TCP TCP_KEEPIDLE TCP_KEEPINTVL TCP_KEEPCNT].all? { |c| Socket.const_defined? c }
|
330
317
|
def set_tcp_keepalive(keepalive)
|
331
318
|
return unless keepalive.is_a?(Hash)
|
332
319
|
|
@@ -338,14 +325,13 @@ class Redis
|
|
338
325
|
|
339
326
|
def get_tcp_keepalive
|
340
327
|
{
|
341
|
-
:
|
342
|
-
:
|
343
|
-
:
|
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
|
344
331
|
}
|
345
332
|
end
|
346
333
|
else
|
347
|
-
def set_tcp_keepalive(keepalive)
|
348
|
-
end
|
334
|
+
def set_tcp_keepalive(keepalive); end
|
349
335
|
|
350
336
|
def get_tcp_keepalive
|
351
337
|
{
|
@@ -354,13 +340,12 @@ class Redis
|
|
354
340
|
end
|
355
341
|
|
356
342
|
# disables Nagle's Algorithm, prevents multiple round trips with MULTI
|
357
|
-
if [
|
343
|
+
if %i[IPPROTO_TCP TCP_NODELAY].all? { |c| Socket.const_defined? c }
|
358
344
|
def set_tcp_nodelay
|
359
345
|
@sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
360
346
|
end
|
361
347
|
else
|
362
|
-
def set_tcp_nodelay
|
363
|
-
end
|
348
|
+
def set_tcp_nodelay; end
|
364
349
|
end
|
365
350
|
|
366
351
|
def initialize(sock)
|
@@ -368,7 +353,7 @@ class Redis
|
|
368
353
|
end
|
369
354
|
|
370
355
|
def connected?
|
371
|
-
|
356
|
+
!!@sock
|
372
357
|
end
|
373
358
|
|
374
359
|
def disconnect
|
@@ -379,9 +364,7 @@ class Redis
|
|
379
364
|
end
|
380
365
|
|
381
366
|
def timeout=(timeout)
|
382
|
-
if @sock.respond_to?(:timeout=)
|
383
|
-
@sock.timeout = timeout
|
384
|
-
end
|
367
|
+
@sock.timeout = timeout if @sock.respond_to?(:timeout=)
|
385
368
|
end
|
386
369
|
|
387
370
|
def write_timeout=(timeout)
|
@@ -396,7 +379,6 @@ class Redis
|
|
396
379
|
line = @sock.gets
|
397
380
|
reply_type = line.slice!(0, 1)
|
398
381
|
format_reply(reply_type, line)
|
399
|
-
|
400
382
|
rescue Errno::EAGAIN
|
401
383
|
raise TimeoutError
|
402
384
|
end
|
@@ -408,7 +390,7 @@ class Redis
|
|
408
390
|
when COLON then format_integer_reply(line)
|
409
391
|
when DOLLAR then format_bulk_reply(line)
|
410
392
|
when ASTERISK then format_multi_bulk_reply(line)
|
411
|
-
else raise ProtocolError
|
393
|
+
else raise ProtocolError, reply_type
|
412
394
|
end
|
413
395
|
end
|
414
396
|
|
@@ -427,6 +409,7 @@ class Redis
|
|
427
409
|
def format_bulk_reply(line)
|
428
410
|
bulklen = line.to_i
|
429
411
|
return if bulklen == -1
|
412
|
+
|
430
413
|
reply = encode(@sock.read(bulklen))
|
431
414
|
@sock.read(2) # Discard CRLF.
|
432
415
|
reply
|
@@ -1,10 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require_relative "command_helper"
|
3
4
|
require_relative "registry"
|
4
5
|
require_relative "../errors"
|
5
6
|
require "em-synchrony"
|
6
7
|
require "hiredis/reader"
|
7
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
|
+
|
8
14
|
class Redis
|
9
15
|
module Connection
|
10
16
|
class RedisClient < EventMachine::Connection
|
@@ -47,9 +53,7 @@ class Redis
|
|
47
53
|
|
48
54
|
def read
|
49
55
|
@req = EventMachine::DefaultDeferrable.new
|
50
|
-
if @timeout > 0
|
51
|
-
@req.timeout(@timeout, :timeout)
|
52
|
-
end
|
56
|
+
@req.timeout(@timeout, :timeout) if @timeout > 0
|
53
57
|
EventMachine::Synchrony.sync @req
|
54
58
|
end
|
55
59
|
|
@@ -106,7 +110,7 @@ class Redis
|
|
106
110
|
end
|
107
111
|
|
108
112
|
def connected?
|
109
|
-
@connection
|
113
|
+
@connection&.connected?
|
110
114
|
end
|
111
115
|
|
112
116
|
def timeout=(timeout)
|