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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b23930fc431afe23313aca3ef90ba49f219a6ed1a50f8153075a09b4c43678c0
4
- data.tar.gz: 502388c79068a1dfe166543a6eb91cead665f42e404f679a598f5a8e2844ee82
3
+ metadata.gz: 2b3a285f6dbfb718f7fa429e962dbb955f3109cf67d785869076c8f0407d7d45
4
+ data.tar.gz: e7e56c056e9eaabc96e438474c70bd2a3ff46a386c28cc891c4b13acbd255492
5
5
  SHA512:
6
- metadata.gz: 9f6d4422b8c3e6608a5a8c53a55335ad0c44c034bc2cf26c7b5aace8dfdbe799409dd9783bd5928da3453d60ed28579594676f95c951a6a4dfff5e04dd09d28d
7
- data.tar.gz: 3ee257ab6d509606c959079a16886389d76bfe232876aba9de1ae8922031746ad2576224fc5b0fae44a09ef13ba4d14ea862098860f923f432f63454499c1677
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- redis-client (0.5.1)
4
+ redis-client (0.6.2)
5
5
  connection_pool
6
6
 
7
7
  GEM
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` returns `nil`.
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!(args, kwargs)
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.each do |key, value|
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!(args, kwargs)
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.each do |key, value|
58
+ kwargs&.each do |key, value|
63
59
  if value
64
60
  if value == true
65
61
  command << key.to_s
@@ -12,8 +12,8 @@ class RedisClient
12
12
  DEFAULT_DB = 0
13
13
 
14
14
  module Common
15
- attr_reader :db, :username, :password, :id, :ssl, :ssl_params, :command_builder,
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 || DEFAULT_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
- RedisClient.new(self, **kwargs)
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
- prelude << if @password
98
- ["HELLO", "3", "AUTH", @username, @password]
99
- else
100
- ["HELLO", "3"]
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}
@@ -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: 128)
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)).to_set
143
+ parse_sequence(io, parse_integer(io))
148
144
  end
149
145
 
150
146
  def parse_map(io)
151
- Hash[*parse_sequence(io, parse_integer(io) * 2)]
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 ReadTimeoutError
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 WriteTimeoutError
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 ConnectionError, error.message
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
- raise ConnectionError, "Couldn't locate a master for role: #{@name}"
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.reject { |r| r["flags"].to_s.split(",").include?("disconnected") }.sample
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
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class RedisClient
4
- VERSION = "0.5.1"
4
+ VERSION = "0.6.2"
5
5
  end
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
- ConnectTimeoutError = Class.new(TimeoutError)
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!(command, kwargs)
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!(command, kwargs)
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!(command, kwargs)
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 block_given?
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!(["SCAN", 0] + args, kwargs)
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!(["SSCAN", key, 0] + args, kwargs)
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!(["HSCAN", key, 0] + args, kwargs)
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!(["ZSCAN", key, 0] + args, kwargs)
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!(command, kwargs))
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!(command, kwargs)
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!(command, kwargs)
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
- if results
421
- results.each_with_index do |result, index|
422
- if result.is_a?(CommandError)
423
- result._set_command(@commands[index + 1])
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&.each_with_index do |block, index|
429
- if block
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!(command, kwargs)
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
- connection.call_pipelined(prelude, nil)
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.5.1
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-06-02 00:00:00.000000000 Z
11
+ date: 2022-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: connection_pool