redis-client 0.5.1 → 0.6.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 +4 -4
- data/CHANGELOG.md +19 -0
- data/Gemfile.lock +1 -1
- data/README.md +12 -1
- data/Rakefile +2 -2
- data/lib/redis_client/command_builder.rb +4 -8
- data/lib/redis_client/config.rb +30 -11
- data/lib/redis_client/decorator.rb +2 -2
- data/lib/redis_client/pooled.rb +1 -1
- data/lib/redis_client/ruby_connection/resp3.rb +11 -7
- data/lib/redis_client/ruby_connection.rb +5 -5
- data/lib/redis_client/sentinel_config.rb +27 -5
- data/lib/redis_client/version.rb +1 -1
- data/lib/redis_client.rb +121 -32
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2b3a285f6dbfb718f7fa429e962dbb955f3109cf67d785869076c8f0407d7d45
|
4
|
+
data.tar.gz: e7e56c056e9eaabc96e438474c70bd2a3ff46a386c28cc891c4b13acbd255492
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0f70dfa045ca15360ba86f18cfa278a98296531b20b661a39abc8788091ff34f5407ec084e48fac45e1a2362f9741512ab35bfcc93d3fd16b3170fd188c57373
|
7
|
+
data.tar.gz: eee07f8f34ba002f46201e50d2d72ff6f05192ccaa06ae23e952bde1008f2b33783afda48ac37ad70d512619395ac442d129d305adf78c17eced954edd9922fa
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,24 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 0.6.2
|
4
|
+
|
5
|
+
- Fix sentinel to not connected to s_down or o_down replicas.
|
6
|
+
|
7
|
+
# 0.6.1
|
8
|
+
|
9
|
+
- Fix `REDIS_REPLY_SET` parsing in `hiredis`.
|
10
|
+
|
11
|
+
# 0.6.0
|
12
|
+
|
13
|
+
- Added `protocol: 2` options to talk with Redis 5 and older servers.
|
14
|
+
- Added `_v` versions of `call` methods to make it easier to pass commands as arrays without splating.
|
15
|
+
- Fix calling `blocking_call` with a block in a pipeline.
|
16
|
+
- `blocking_call` now raise `ReadTimeoutError` if the command didn't complete in time.
|
17
|
+
- Fix `blocking_call` to not respect `retry_attempts` on timeout.
|
18
|
+
- Stop parsing RESP3 sets as Ruby Set instances.
|
19
|
+
- Fix `SystemStackError` when parsing very large hashes. Fix: #30
|
20
|
+
- `hiredis` now more properly release the GVL when doing IOs.
|
21
|
+
|
3
22
|
# 0.5.1
|
4
23
|
|
5
24
|
- Fix a regression in the `scan` familly of methods, they would raise with `ArgumentError: can't issue an empty redis command`. Fix: #24
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -81,6 +81,7 @@ redis.call("GET", "mykey")
|
|
81
81
|
- `read_timeout`: The read timeout, takes precedence over the general timeout when reading responses from the server.
|
82
82
|
- `write_timeout`: The write timeout, takes precedence over the general timeout when sending commands to the server.
|
83
83
|
- `reconnect_attempts`: Specify how many times the client should retry to send queries. Defaults to `0`. Makes sure to read the [reconnection section](#reconnection) before enabling it.
|
84
|
+
- `protocol:` The version of the RESP protocol to use. Default to `3`.
|
84
85
|
|
85
86
|
### Sentinel support
|
86
87
|
|
@@ -207,6 +208,16 @@ redis.call("EXISTS", "counter") # => 1
|
|
207
208
|
redis.call("EXISTS", "counter") { |c| c > 0 } # => true
|
208
209
|
```
|
209
210
|
|
211
|
+
### `*_v` methods
|
212
|
+
|
213
|
+
In some it's more convenient to pass commands as arrays, for that `_v` versions of `call` methods are available.
|
214
|
+
|
215
|
+
```ruby
|
216
|
+
redis.call_v(["MGET"] + keys)
|
217
|
+
redis.blocking_call_v(1, ["MGET"] + keys)
|
218
|
+
redis.call_once_v(1, ["MGET"] + keys)
|
219
|
+
```
|
220
|
+
|
210
221
|
### Blocking commands
|
211
222
|
|
212
223
|
For blocking commands such as `BRPOP`, a custom timeout duration can be passed as first argument of the `#blocking_call` method:
|
@@ -215,7 +226,7 @@ For blocking commands such as `BRPOP`, a custom timeout duration can be passed a
|
|
215
226
|
redis.blocking_call(timeout, "BRPOP", "key", 0)
|
216
227
|
```
|
217
228
|
|
218
|
-
If `timeout` is reached, `#blocking_call`
|
229
|
+
If `timeout` is reached, `#blocking_call` raises `RedisClient::ReadTimeoutError` and doesn't retry regardless of the `reconnect_attempts` configuration.
|
219
230
|
|
220
231
|
`timeout` is expressed in seconds, you can pass `false` or `0` to mean no timeout.
|
221
232
|
|
data/Rakefile
CHANGED
@@ -107,8 +107,8 @@ end
|
|
107
107
|
|
108
108
|
if hiredis_supported
|
109
109
|
task default: %i[compile test rubocop]
|
110
|
-
task ci: %i[compile test]
|
110
|
+
task ci: %i[compile test:ruby test:hiredis]
|
111
111
|
else
|
112
112
|
task default: %i[test rubocop]
|
113
|
-
task ci: %i[test]
|
113
|
+
task ci: %i[test:ruby]
|
114
114
|
end
|
@@ -5,19 +5,17 @@ class RedisClient
|
|
5
5
|
extend self
|
6
6
|
|
7
7
|
if Symbol.method_defined?(:name)
|
8
|
-
def generate
|
8
|
+
def generate(args, kwargs = nil)
|
9
9
|
command = args.flat_map do |element|
|
10
10
|
case element
|
11
11
|
when Hash
|
12
12
|
element.flatten
|
13
|
-
when Set
|
14
|
-
element.to_a
|
15
13
|
else
|
16
14
|
element
|
17
15
|
end
|
18
16
|
end
|
19
17
|
|
20
|
-
kwargs
|
18
|
+
kwargs&.each do |key, value|
|
21
19
|
if value
|
22
20
|
if value == true
|
23
21
|
command << key.name
|
@@ -47,19 +45,17 @@ class RedisClient
|
|
47
45
|
command
|
48
46
|
end
|
49
47
|
else
|
50
|
-
def generate
|
48
|
+
def generate(args, kwargs = nil)
|
51
49
|
command = args.flat_map do |element|
|
52
50
|
case element
|
53
51
|
when Hash
|
54
52
|
element.flatten
|
55
|
-
when Set
|
56
|
-
element.to_a
|
57
53
|
else
|
58
54
|
element
|
59
55
|
end
|
60
56
|
end
|
61
57
|
|
62
|
-
kwargs
|
58
|
+
kwargs&.each do |key, value|
|
63
59
|
if value
|
64
60
|
if value == true
|
65
61
|
command << key.to_s
|
data/lib/redis_client/config.rb
CHANGED
@@ -12,8 +12,8 @@ class RedisClient
|
|
12
12
|
DEFAULT_DB = 0
|
13
13
|
|
14
14
|
module Common
|
15
|
-
attr_reader :db, :
|
16
|
-
:connect_timeout, :read_timeout, :write_timeout, :driver, :connection_prelude
|
15
|
+
attr_reader :db, :password, :id, :ssl, :ssl_params, :command_builder,
|
16
|
+
:connect_timeout, :read_timeout, :write_timeout, :driver, :connection_prelude, :protocol
|
17
17
|
|
18
18
|
alias_method :ssl?, :ssl
|
19
19
|
|
@@ -29,10 +29,12 @@ class RedisClient
|
|
29
29
|
ssl: nil,
|
30
30
|
ssl_params: nil,
|
31
31
|
driver: nil,
|
32
|
+
protocol: 3,
|
33
|
+
client_implementation: RedisClient,
|
32
34
|
command_builder: CommandBuilder,
|
33
35
|
reconnect_attempts: false
|
34
36
|
)
|
35
|
-
@username = username
|
37
|
+
@username = username
|
36
38
|
@password = password
|
37
39
|
@db = db || DEFAULT_DB
|
38
40
|
@id = id
|
@@ -45,14 +47,23 @@ class RedisClient
|
|
45
47
|
|
46
48
|
@driver = driver ? RedisClient.driver(driver) : RedisClient.default_driver
|
47
49
|
|
50
|
+
@client_implementation = client_implementation
|
51
|
+
@protocol = protocol
|
52
|
+
unless protocol == 2 || protocol == 3
|
53
|
+
raise ArgumentError, "Unknown protocol version #{protocol.inspect}, expected 2 or 3"
|
54
|
+
end
|
55
|
+
|
48
56
|
@command_builder = command_builder
|
49
57
|
|
50
58
|
reconnect_attempts = Array.new(reconnect_attempts, 0).freeze if reconnect_attempts.is_a?(Integer)
|
51
59
|
@reconnect_attempts = reconnect_attempts
|
52
|
-
|
53
60
|
@connection_prelude = build_connection_prelude
|
54
61
|
end
|
55
62
|
|
63
|
+
def username
|
64
|
+
@username || DEFAULT_USERNAME
|
65
|
+
end
|
66
|
+
|
56
67
|
def sentinel?
|
57
68
|
false
|
58
69
|
end
|
@@ -63,7 +74,7 @@ class RedisClient
|
|
63
74
|
end
|
64
75
|
|
65
76
|
def new_client(**kwargs)
|
66
|
-
|
77
|
+
@client_implementation.new(self, **kwargs)
|
67
78
|
end
|
68
79
|
|
69
80
|
def retry_connecting?(attempt, _error)
|
@@ -79,7 +90,7 @@ class RedisClient
|
|
79
90
|
end
|
80
91
|
|
81
92
|
def ssl_context
|
82
|
-
@ssl_context ||= @driver.ssl_context(@ssl_params)
|
93
|
+
@ssl_context ||= @driver.ssl_context(@ssl_params || {})
|
83
94
|
end
|
84
95
|
|
85
96
|
def server_url
|
@@ -94,10 +105,18 @@ class RedisClient
|
|
94
105
|
|
95
106
|
def build_connection_prelude
|
96
107
|
prelude = []
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
108
|
+
if protocol == 3
|
109
|
+
prelude << if @password
|
110
|
+
["HELLO", "3", "AUTH", @username || DEFAULT_USERNAME, @password]
|
111
|
+
else
|
112
|
+
["HELLO", "3"]
|
113
|
+
end
|
114
|
+
elsif @password
|
115
|
+
prelude << if @username && !@username.empty?
|
116
|
+
["AUTH", @username, @password]
|
117
|
+
else
|
118
|
+
["AUTH", @password]
|
119
|
+
end
|
101
120
|
end
|
102
121
|
|
103
122
|
if @db && @db != 0
|
@@ -136,7 +155,7 @@ class RedisClient
|
|
136
155
|
super(**kwargs)
|
137
156
|
|
138
157
|
@host = host || uri&.host&.sub(/\A\[(.*)\]\z/, '\1') || DEFAULT_HOST
|
139
|
-
@port = port || uri&.port || DEFAULT_PORT
|
158
|
+
@port = Integer(port || uri&.port || DEFAULT_PORT)
|
140
159
|
@path = path
|
141
160
|
end
|
142
161
|
end
|
@@ -20,7 +20,7 @@ class RedisClient
|
|
20
20
|
@client = client
|
21
21
|
end
|
22
22
|
|
23
|
-
%i(call call_once blocking_call).each do |method|
|
23
|
+
%i(call call_v call_once call_once_v blocking_call blocking_call_v).each do |method|
|
24
24
|
class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
25
25
|
def #{method}(*args, &block)
|
26
26
|
@client.#{method}(*args, &block)
|
@@ -64,7 +64,7 @@ class RedisClient
|
|
64
64
|
RUBY
|
65
65
|
end
|
66
66
|
|
67
|
-
%i(id config size connect_timeout read_timeout write_timeout).each do |reader|
|
67
|
+
%i(id config size connect_timeout read_timeout write_timeout pubsub).each do |reader|
|
68
68
|
class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
69
69
|
def #{reader}
|
70
70
|
@client.#{reader}
|
data/lib/redis_client/pooled.rb
CHANGED
@@ -49,7 +49,7 @@ class RedisClient
|
|
49
49
|
pool.size
|
50
50
|
end
|
51
51
|
|
52
|
-
methods = %w(pipelined multi pubsub call call_once blocking_call)
|
52
|
+
methods = %w(pipelined multi pubsub call call_v call_once call_once_v blocking_call blocking_call_v)
|
53
53
|
iterable_methods = %w(scan sscan hscan zscan)
|
54
54
|
begin
|
55
55
|
methods.each do |method|
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "set"
|
4
|
-
|
5
3
|
class RedisClient
|
6
4
|
module RESP3
|
7
5
|
module_function
|
@@ -41,8 +39,6 @@ class RedisClient
|
|
41
39
|
case element
|
42
40
|
when Hash
|
43
41
|
element.flatten
|
44
|
-
when Set
|
45
|
-
element.to_a
|
46
42
|
else
|
47
43
|
element
|
48
44
|
end
|
@@ -55,7 +51,7 @@ class RedisClient
|
|
55
51
|
end
|
56
52
|
|
57
53
|
def new_buffer
|
58
|
-
String.new(encoding: Encoding::BINARY, capacity:
|
54
|
+
String.new(encoding: Encoding::BINARY, capacity: 127)
|
59
55
|
end
|
60
56
|
|
61
57
|
def dump_any(object, buffer)
|
@@ -144,11 +140,15 @@ class RedisClient
|
|
144
140
|
end
|
145
141
|
|
146
142
|
def parse_set(io)
|
147
|
-
parse_sequence(io, parse_integer(io))
|
143
|
+
parse_sequence(io, parse_integer(io))
|
148
144
|
end
|
149
145
|
|
150
146
|
def parse_map(io)
|
151
|
-
|
147
|
+
hash = {}
|
148
|
+
parse_integer(io).times do
|
149
|
+
hash[parse(io)] = parse(io)
|
150
|
+
end
|
151
|
+
hash
|
152
152
|
end
|
153
153
|
|
154
154
|
def parse_push(io)
|
@@ -156,6 +156,8 @@ class RedisClient
|
|
156
156
|
end
|
157
157
|
|
158
158
|
def parse_sequence(io, size)
|
159
|
+
return if size < 0 # RESP2 nil
|
160
|
+
|
159
161
|
array = Array.new(size)
|
160
162
|
size.times do |index|
|
161
163
|
array[index] = parse(io)
|
@@ -185,6 +187,8 @@ class RedisClient
|
|
185
187
|
|
186
188
|
def parse_blob(io)
|
187
189
|
bytesize = parse_integer(io)
|
190
|
+
return if bytesize < 0 # RESP2 nil type
|
191
|
+
|
188
192
|
str = io.read_chomp(bytesize)
|
189
193
|
str.force_encoding(Encoding.default_external)
|
190
194
|
str.force_encoding(Encoding::BINARY) unless str.valid_encoding?
|
@@ -60,9 +60,9 @@ class RedisClient
|
|
60
60
|
loop do
|
61
61
|
case status = socket.connect_nonblock(exception: false)
|
62
62
|
when :wait_readable
|
63
|
-
socket.to_io.wait_readable(connect_timeout) or raise
|
63
|
+
socket.to_io.wait_readable(connect_timeout) or raise CannotConnectError
|
64
64
|
when :wait_writable
|
65
|
-
socket.to_io.wait_writable(connect_timeout) or raise
|
65
|
+
socket.to_io.wait_writable(connect_timeout) or raise CannotConnectError
|
66
66
|
when socket
|
67
67
|
break
|
68
68
|
else
|
@@ -76,10 +76,8 @@ class RedisClient
|
|
76
76
|
read_timeout: read_timeout,
|
77
77
|
write_timeout: write_timeout,
|
78
78
|
)
|
79
|
-
rescue Errno::ETIMEDOUT => error
|
80
|
-
raise ConnectTimeoutError, error.message
|
81
79
|
rescue SystemCallError, OpenSSL::SSL::SSLError, SocketError => error
|
82
|
-
raise
|
80
|
+
raise CannotConnectError, error.message, error.backtrace
|
83
81
|
end
|
84
82
|
|
85
83
|
def connected?
|
@@ -125,6 +123,8 @@ class RedisClient
|
|
125
123
|
else
|
126
124
|
@io.with_timeout(timeout) { RESP3.load(@io) }
|
127
125
|
end
|
126
|
+
rescue RedisClient::RESP3::UnknownType => error
|
127
|
+
raise RedisClient::ProtocolError, error.message
|
128
128
|
rescue SystemCallError, IOError, OpenSSL::SSL::SSLError => error
|
129
129
|
raise ConnectionError, error.message
|
130
130
|
end
|
@@ -12,8 +12,21 @@ class RedisClient
|
|
12
12
|
raise ArgumentError, "Expected role to be either :master or :replica, got: #{role.inspect}"
|
13
13
|
end
|
14
14
|
|
15
|
+
@to_list_of_hash = @to_hash = nil
|
16
|
+
extra_config = {}
|
17
|
+
if client_config[:protocol] == 2
|
18
|
+
extra_config[:protocol] = client_config[:protocol]
|
19
|
+
@to_list_of_hash = lambda do |may_be_a_list|
|
20
|
+
if may_be_a_list.is_a?(Array)
|
21
|
+
may_be_a_list.map { |l| l.each_slice(2).to_h }
|
22
|
+
else
|
23
|
+
may_be_a_list
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
15
28
|
@name = name
|
16
|
-
@sentinel_configs = sentinels.map { |s| Config.new(**s) }
|
29
|
+
@sentinel_configs = sentinels.map { |s| Config.new(**extra_config, **s) }
|
17
30
|
@sentinels = {}.compare_by_identity
|
18
31
|
@role = role
|
19
32
|
@mutex = Mutex.new
|
@@ -90,7 +103,10 @@ class RedisClient
|
|
90
103
|
return Config.new(host: host, port: Integer(port), **@client_config)
|
91
104
|
end
|
92
105
|
end
|
93
|
-
|
106
|
+
rescue ConnectionError
|
107
|
+
raise ConnectionError, "No sentinels available"
|
108
|
+
else
|
109
|
+
raise ConnectionError, "Couldn't locate a replica for role: #{@name}"
|
94
110
|
end
|
95
111
|
|
96
112
|
def sentinel_client(sentinel_config)
|
@@ -99,13 +115,19 @@ class RedisClient
|
|
99
115
|
|
100
116
|
def resolve_replica
|
101
117
|
each_sentinel do |sentinel_client|
|
102
|
-
replicas = sentinel_client.call("SENTINEL", "replicas", @name)
|
118
|
+
replicas = sentinel_client.call("SENTINEL", "replicas", @name, &@to_list_of_hash)
|
119
|
+
replicas.reject! do |r|
|
120
|
+
flags = r["flags"].to_s.split(",")
|
121
|
+
flags.include?("s_down") || flags.include?("o_down")
|
122
|
+
end
|
103
123
|
next if replicas.empty?
|
104
124
|
|
105
|
-
replica = replicas.
|
106
|
-
replica ||= replicas.sample
|
125
|
+
replica = replicas.sample
|
107
126
|
return Config.new(host: replica["ip"], port: Integer(replica["port"]), **@client_config)
|
108
127
|
end
|
128
|
+
rescue ConnectionError
|
129
|
+
raise ConnectionError, "No sentinels available"
|
130
|
+
else
|
109
131
|
raise ConnectionError, "Couldn't locate a replica for role: #{@name}"
|
110
132
|
end
|
111
133
|
|
data/lib/redis_client/version.rb
CHANGED
data/lib/redis_client.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "set"
|
4
|
-
|
5
3
|
require "redis_client/version"
|
6
4
|
require "redis_client/command_builder"
|
7
5
|
require "redis_client/config"
|
@@ -78,17 +76,18 @@ class RedisClient
|
|
78
76
|
|
79
77
|
Error = Class.new(StandardError)
|
80
78
|
|
79
|
+
ProtocolError = Class.new(Error)
|
81
80
|
UnsupportedServer = Class.new(Error)
|
82
81
|
|
83
82
|
ConnectionError = Class.new(Error)
|
83
|
+
CannotConnectError = Class.new(ConnectionError)
|
84
84
|
|
85
85
|
FailoverError = Class.new(ConnectionError)
|
86
86
|
|
87
87
|
TimeoutError = Class.new(ConnectionError)
|
88
88
|
ReadTimeoutError = Class.new(TimeoutError)
|
89
89
|
WriteTimeoutError = Class.new(TimeoutError)
|
90
|
-
|
91
|
-
CheckoutTimeoutError = Class.new(ConnectTimeoutError)
|
90
|
+
CheckoutTimeoutError = Class.new(TimeoutError)
|
92
91
|
|
93
92
|
class CommandError < Error
|
94
93
|
attr_reader :command
|
@@ -120,11 +119,11 @@ class RedisClient
|
|
120
119
|
|
121
120
|
class << self
|
122
121
|
def config(**kwargs)
|
123
|
-
Config.new(**kwargs)
|
122
|
+
Config.new(client_implementation: self, **kwargs)
|
124
123
|
end
|
125
124
|
|
126
125
|
def sentinel(**kwargs)
|
127
|
-
SentinelConfig.new(**kwargs)
|
126
|
+
SentinelConfig.new(client_implementation: self, **kwargs)
|
128
127
|
end
|
129
128
|
|
130
129
|
def new(arg = nil, **kwargs)
|
@@ -184,7 +183,22 @@ class RedisClient
|
|
184
183
|
end
|
185
184
|
|
186
185
|
def call(*command, **kwargs)
|
187
|
-
command = @command_builder.generate
|
186
|
+
command = @command_builder.generate(command, kwargs)
|
187
|
+
result = ensure_connected do |connection|
|
188
|
+
Middlewares.call(command, config) do
|
189
|
+
connection.call(command, nil)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
if block_given?
|
194
|
+
yield result
|
195
|
+
else
|
196
|
+
result
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def call_v(command)
|
201
|
+
command = @command_builder.generate(command)
|
188
202
|
result = ensure_connected do |connection|
|
189
203
|
Middlewares.call(command, config) do
|
190
204
|
connection.call(command, nil)
|
@@ -199,7 +213,22 @@ class RedisClient
|
|
199
213
|
end
|
200
214
|
|
201
215
|
def call_once(*command, **kwargs)
|
202
|
-
command = @command_builder.generate
|
216
|
+
command = @command_builder.generate(command, kwargs)
|
217
|
+
result = ensure_connected(retryable: false) do |connection|
|
218
|
+
Middlewares.call(command, config) do
|
219
|
+
connection.call(command, nil)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
if block_given?
|
224
|
+
yield result
|
225
|
+
else
|
226
|
+
result
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def call_once_v(command)
|
231
|
+
command = @command_builder.generate(command)
|
203
232
|
result = ensure_connected(retryable: false) do |connection|
|
204
233
|
Middlewares.call(command, config) do
|
205
234
|
connection.call(command, nil)
|
@@ -214,14 +243,39 @@ class RedisClient
|
|
214
243
|
end
|
215
244
|
|
216
245
|
def blocking_call(timeout, *command, **kwargs)
|
217
|
-
command = @command_builder.generate
|
246
|
+
command = @command_builder.generate(command, kwargs)
|
247
|
+
error = nil
|
218
248
|
result = ensure_connected do |connection|
|
219
249
|
Middlewares.call(command, config) do
|
220
250
|
connection.call(command, timeout)
|
221
251
|
end
|
252
|
+
rescue ReadTimeoutError => error
|
253
|
+
break
|
222
254
|
end
|
223
255
|
|
224
|
-
if
|
256
|
+
if error
|
257
|
+
raise error
|
258
|
+
elsif block_given?
|
259
|
+
yield result
|
260
|
+
else
|
261
|
+
result
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def blocking_call_v(timeout, command)
|
266
|
+
command = @command_builder.generate(command)
|
267
|
+
error = nil
|
268
|
+
result = ensure_connected do |connection|
|
269
|
+
Middlewares.call(command, config) do
|
270
|
+
connection.call(command, timeout)
|
271
|
+
end
|
272
|
+
rescue ReadTimeoutError => error
|
273
|
+
break
|
274
|
+
end
|
275
|
+
|
276
|
+
if error
|
277
|
+
raise error
|
278
|
+
elsif block_given?
|
225
279
|
yield result
|
226
280
|
else
|
227
281
|
result
|
@@ -233,7 +287,7 @@ class RedisClient
|
|
233
287
|
return to_enum(__callee__, *args, **kwargs)
|
234
288
|
end
|
235
289
|
|
236
|
-
args = @command_builder.generate
|
290
|
+
args = @command_builder.generate(["SCAN", 0] + args, kwargs)
|
237
291
|
scan_list(1, args, &block)
|
238
292
|
end
|
239
293
|
|
@@ -242,7 +296,7 @@ class RedisClient
|
|
242
296
|
return to_enum(__callee__, key, *args, **kwargs)
|
243
297
|
end
|
244
298
|
|
245
|
-
args = @command_builder.generate
|
299
|
+
args = @command_builder.generate(["SSCAN", key, 0] + args, kwargs)
|
246
300
|
scan_list(2, args, &block)
|
247
301
|
end
|
248
302
|
|
@@ -251,7 +305,7 @@ class RedisClient
|
|
251
305
|
return to_enum(__callee__, key, *args, **kwargs)
|
252
306
|
end
|
253
307
|
|
254
|
-
args = @command_builder.generate
|
308
|
+
args = @command_builder.generate(["HSCAN", key, 0] + args, kwargs)
|
255
309
|
scan_pairs(2, args, &block)
|
256
310
|
end
|
257
311
|
|
@@ -260,7 +314,7 @@ class RedisClient
|
|
260
314
|
return to_enum(__callee__, key, *args, **kwargs)
|
261
315
|
end
|
262
316
|
|
263
|
-
args = @command_builder.generate
|
317
|
+
args = @command_builder.generate(["ZSCAN", key, 0] + args, kwargs)
|
264
318
|
scan_pairs(2, args, &block)
|
265
319
|
end
|
266
320
|
|
@@ -343,7 +397,12 @@ class RedisClient
|
|
343
397
|
end
|
344
398
|
|
345
399
|
def call(*command, **kwargs)
|
346
|
-
raw_connection.write(@command_builder.generate
|
400
|
+
raw_connection.write(@command_builder.generate(command, kwargs))
|
401
|
+
nil
|
402
|
+
end
|
403
|
+
|
404
|
+
def call_v(command)
|
405
|
+
raw_connection.write(@command_builder.generate(command))
|
347
406
|
nil
|
348
407
|
end
|
349
408
|
|
@@ -378,14 +437,29 @@ class RedisClient
|
|
378
437
|
end
|
379
438
|
|
380
439
|
def call(*command, **kwargs, &block)
|
381
|
-
command = @command_builder.generate
|
440
|
+
command = @command_builder.generate(command, kwargs)
|
441
|
+
(@blocks ||= [])[@commands.size] = block if block_given?
|
442
|
+
@commands << command
|
443
|
+
nil
|
444
|
+
end
|
445
|
+
|
446
|
+
def call_v(command, &block)
|
447
|
+
command = @command_builder.generate(command)
|
382
448
|
(@blocks ||= [])[@commands.size] = block if block_given?
|
383
449
|
@commands << command
|
384
450
|
nil
|
385
451
|
end
|
386
452
|
|
387
|
-
def call_once(*command, **kwargs)
|
388
|
-
command = @command_builder.generate
|
453
|
+
def call_once(*command, **kwargs, &block)
|
454
|
+
command = @command_builder.generate(command, kwargs)
|
455
|
+
@retryable = false
|
456
|
+
(@blocks ||= [])[@commands.size] = block if block_given?
|
457
|
+
@commands << command
|
458
|
+
nil
|
459
|
+
end
|
460
|
+
|
461
|
+
def call_once_v(command, &block)
|
462
|
+
command = @command_builder.generate(command)
|
389
463
|
@retryable = false
|
390
464
|
(@blocks ||= [])[@commands.size] = block if block_given?
|
391
465
|
@commands << command
|
@@ -417,18 +491,14 @@ class RedisClient
|
|
417
491
|
end
|
418
492
|
|
419
493
|
def _coerce!(results)
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
raise result
|
425
|
-
end
|
494
|
+
results&.each_with_index do |result, index|
|
495
|
+
if result.is_a?(CommandError)
|
496
|
+
result._set_command(@commands[index + 1])
|
497
|
+
raise result
|
426
498
|
end
|
427
499
|
|
428
|
-
@blocks
|
429
|
-
|
430
|
-
results[index - 1] = block.call(results[index - 1])
|
431
|
-
end
|
500
|
+
if @blocks && block = @blocks[index + 1]
|
501
|
+
results[index] = block.call(result)
|
432
502
|
end
|
433
503
|
end
|
434
504
|
|
@@ -442,8 +512,17 @@ class RedisClient
|
|
442
512
|
@timeouts = nil
|
443
513
|
end
|
444
514
|
|
445
|
-
def blocking_call(timeout, *command, **kwargs)
|
446
|
-
command = @command_builder.generate
|
515
|
+
def blocking_call(timeout, *command, **kwargs, &block)
|
516
|
+
command = @command_builder.generate(command, kwargs)
|
517
|
+
@timeouts ||= []
|
518
|
+
@timeouts[@commands.size] = timeout
|
519
|
+
(@blocks ||= [])[@commands.size] = block if block_given?
|
520
|
+
@commands << command
|
521
|
+
nil
|
522
|
+
end
|
523
|
+
|
524
|
+
def blocking_call_v(timeout, command, &block)
|
525
|
+
command = @command_builder.generate(command)
|
447
526
|
@timeouts ||= []
|
448
527
|
@timeouts[@commands.size] = timeout
|
449
528
|
(@blocks ||= [])[@commands.size] = block if block_given?
|
@@ -521,7 +600,7 @@ class RedisClient
|
|
521
600
|
else
|
522
601
|
connection
|
523
602
|
end
|
524
|
-
rescue ConnectionError => error
|
603
|
+
rescue ConnectionError, ProtocolError => error
|
525
604
|
connection&.close
|
526
605
|
close
|
527
606
|
|
@@ -538,6 +617,10 @@ class RedisClient
|
|
538
617
|
begin
|
539
618
|
@disable_reconnection = true
|
540
619
|
yield connection
|
620
|
+
rescue ConnectionError, ProtocolError
|
621
|
+
connection&.close
|
622
|
+
close
|
623
|
+
raise
|
541
624
|
ensure
|
542
625
|
@disable_reconnection = previous_disable_reconnection
|
543
626
|
end
|
@@ -568,10 +651,16 @@ class RedisClient
|
|
568
651
|
role, = connection.call_pipelined(prelude, nil).last
|
569
652
|
config.check_role!(role)
|
570
653
|
else
|
571
|
-
|
654
|
+
unless prelude.empty?
|
655
|
+
connection.call_pipelined(prelude, nil)
|
656
|
+
end
|
572
657
|
end
|
573
658
|
|
574
659
|
connection
|
660
|
+
rescue FailoverError
|
661
|
+
raise
|
662
|
+
rescue ConnectionError => error
|
663
|
+
raise CannotConnectError, error.message, error.backtrace
|
575
664
|
rescue CommandError => error
|
576
665
|
if error.message.include?("ERR unknown command `HELLO`")
|
577
666
|
raise UnsupportedServer,
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jean Boussier
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-08-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: connection_pool
|