redis-client 0.26.4 → 0.29.0
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 +15 -1
- data/README.md +31 -1
- data/lib/redis_client/config.rb +34 -2
- data/lib/redis_client/connection_mixin.rb +6 -4
- data/lib/redis_client/hash_ring.rb +87 -0
- data/lib/redis_client/sentinel_config.rb +4 -0
- data/lib/redis_client/version.rb +1 -1
- data/lib/redis_client.rb +62 -13
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dc26284bc8ddd661e51d52ecf7afcf6d853b566128baa4176908eed4ed7e6ce2
|
|
4
|
+
data.tar.gz: 4051332c5b82220c88cb43efc0257e6dc61cba6b2c61ace4429d197591ecbd53
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7f200a254286ae8c63dd708f4cc1d945b33a8ec96e5466de8b6fe966ddb2ebdca727cb532d817a05021eabdba9809eb2450a0ad31dfc24e39ae2e4473ca9a36a
|
|
7
|
+
data.tar.gz: 192f624ea10bf819f8d4f98d980861b945cbe5fad8a079dbb0ed247d031b70646eb16cf9a34eb9f0b7b80abb7eee191a3b006bc46a7b0ac8d7b9a9924ee00d1a
|
data/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,22 @@
|
|
|
1
1
|
# Unreleased
|
|
2
2
|
|
|
3
|
+
# 0.29.0
|
|
4
|
+
|
|
5
|
+
- Fix connecting to Redis 7.1 and older with `driver_info:`set.
|
|
6
|
+
- Added `RedisClient::Error#next_error` for an easier way to check all errors produced by a pipeline.
|
|
7
|
+
|
|
8
|
+
# 0.28.0
|
|
9
|
+
|
|
10
|
+
- Added `RedisClient::HashRing` for horizontal sharing (compatible with `Redis::Distributed` from `redis-rb`).
|
|
11
|
+
|
|
12
|
+
# 0.27.0
|
|
13
|
+
|
|
14
|
+
- Added `idle_timeout` to revalidate connections that haven't been successfuly used in a long time. Defaults to 30 seconds.
|
|
15
|
+
- Added `driver_info` configuration, to issue `CLIENT SETINFO` during connection prelude.
|
|
16
|
+
|
|
3
17
|
# 0.26.4
|
|
4
18
|
|
|
5
|
-
- Further improve `rediss://` URLs used with Redis sentinel. Now avoid override explictly set `ssl:`
|
|
19
|
+
- Further improve `rediss://` URLs used with Redis sentinel. Now avoid override explictly set `ssl:` parameter.
|
|
6
20
|
- Fix compatibility with `redis-rb` in sentinel mode.
|
|
7
21
|
|
|
8
22
|
# 0.26.3
|
data/README.md
CHANGED
|
@@ -83,6 +83,7 @@ redis.call("GET", "mykey")
|
|
|
83
83
|
- `connect_timeout`: The connection timeout, takes precedence over the general timeout when connecting to the server.
|
|
84
84
|
- `read_timeout`: The read timeout, takes precedence over the general timeout when reading responses from the server.
|
|
85
85
|
- `write_timeout`: The write timeout, takes precedence over the general timeout when sending commands to the server.
|
|
86
|
+
- `idle_timeout`: Amount of time after which an idle connection has to be revalidated with a PING command. Defaults to `30` seconds.
|
|
86
87
|
- `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.
|
|
87
88
|
- `circuit_breaker`: A Hash with circuit breaker configuration. Defaults to `nil`. See the [circuit breaker section](#circuit-breaker) for details.
|
|
88
89
|
- `protocol:` The version of the RESP protocol to use. Default to `3`.
|
|
@@ -143,6 +144,7 @@ redis_config = RedisClient.sentinel(name: 'mymaster', sentinel_username: 'appuse
|
|
|
143
144
|
If you specify a username and/or password at the top level for your main Redis instance, Sentinel *will not* using thouse credentials
|
|
144
145
|
|
|
145
146
|
```ruby
|
|
147
|
+
|
|
146
148
|
# Use 'mysecret' to authenticate against the mymaster instance, but skip authentication for the sentinels:
|
|
147
149
|
SENTINELS = [{ host: '127.0.0.1', port: 26380 },
|
|
148
150
|
{ host: '127.0.0.1', port: 26381 }]
|
|
@@ -166,6 +168,30 @@ Also the `name`, `password`, `username` and `db` for Redis instance can be passe
|
|
|
166
168
|
redis_config = RedisClient.sentinel(url: "redis://appuser:mysecret@mymaster/10", sentinels: SENTINELS, role: :master)
|
|
167
169
|
```
|
|
168
170
|
|
|
171
|
+
### Consistent Hashing
|
|
172
|
+
|
|
173
|
+
To horizontally shard keys across multiple servers without relying on clustering, a `RedisClient::HashRing` class is provided:
|
|
174
|
+
|
|
175
|
+
```ruby
|
|
176
|
+
ring = RedisClient.ring(
|
|
177
|
+
RedisClient.config(host: "10.0.1.1", port: 6380).new_pool(timeout: 0.5, size: 3),
|
|
178
|
+
RedisClient.config(host: "10.0.1.2", port: 6380).new_pool(timeout: 0.5, size: 3),
|
|
179
|
+
RedisClient.config(host: "10.0.1.3", port: 6380).new_pool(timeout: 0.5, size: 3),
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
ring.node_for("cache_key").call("GET", "cache_key") # => "value"
|
|
183
|
+
|
|
184
|
+
ring.nodes_for("key1", "key2", "key3").each do |node, keys|
|
|
185
|
+
node.call("DEL", *keys)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
ring.nodes.each do |node|
|
|
189
|
+
node.close
|
|
190
|
+
end
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Note that regular clients do respond to `node_for`, `nodes_for` and `nodes`, so that code that support `RedisClient.ring` is also usable with a single server.
|
|
194
|
+
|
|
169
195
|
### Type support
|
|
170
196
|
|
|
171
197
|
Only a select few Ruby types are supported as arguments beside strings.
|
|
@@ -335,7 +361,11 @@ end
|
|
|
335
361
|
|
|
336
362
|
#### Exception management
|
|
337
363
|
|
|
338
|
-
|
|
364
|
+
By default, when a pipeline produce multiple command errors, only the first encountered error is raised.
|
|
365
|
+
|
|
366
|
+
However, the raised exception have a `.next_error` method you can use like a linked list to check all encountered errors.
|
|
367
|
+
|
|
368
|
+
Alternatively, the `exception` flag in the `#pipelined` method of `RedisClient` is a feature that modifies the pipeline execution
|
|
339
369
|
behavior. When set to `false`, it doesn't raise an exception when a command error occurs. Instead, it allows the
|
|
340
370
|
pipeline to execute all commands, and any failed command will be available in the returned array. (Defaults to `true`)
|
|
341
371
|
|
data/lib/redis_client/config.rb
CHANGED
|
@@ -10,11 +10,12 @@ class RedisClient
|
|
|
10
10
|
DEFAULT_PORT = 6379
|
|
11
11
|
DEFAULT_USERNAME = "default"
|
|
12
12
|
DEFAULT_DB = 0
|
|
13
|
+
DEFAULT_IDLE_TIMEOUT = 30.0
|
|
13
14
|
|
|
14
15
|
module Common
|
|
15
16
|
attr_reader :db, :id, :ssl, :ssl_params, :command_builder, :inherit_socket,
|
|
16
17
|
:connect_timeout, :read_timeout, :write_timeout, :driver, :protocol,
|
|
17
|
-
:middlewares_stack, :custom, :circuit_breaker
|
|
18
|
+
:middlewares_stack, :custom, :circuit_breaker, :driver_info, :idle_timeout
|
|
18
19
|
|
|
19
20
|
alias_method :ssl?, :ssl
|
|
20
21
|
|
|
@@ -27,6 +28,7 @@ class RedisClient
|
|
|
27
28
|
read_timeout: timeout,
|
|
28
29
|
write_timeout: timeout,
|
|
29
30
|
connect_timeout: timeout,
|
|
31
|
+
idle_timeout: DEFAULT_IDLE_TIMEOUT,
|
|
30
32
|
ssl: nil,
|
|
31
33
|
custom: {},
|
|
32
34
|
ssl_params: nil,
|
|
@@ -37,7 +39,8 @@ class RedisClient
|
|
|
37
39
|
inherit_socket: false,
|
|
38
40
|
reconnect_attempts: false,
|
|
39
41
|
middlewares: false,
|
|
40
|
-
circuit_breaker: nil
|
|
42
|
+
circuit_breaker: nil,
|
|
43
|
+
driver_info: nil
|
|
41
44
|
)
|
|
42
45
|
@username = username
|
|
43
46
|
@password = password && !password.respond_to?(:call) ? ->(_) { password } : password
|
|
@@ -54,6 +57,7 @@ class RedisClient
|
|
|
54
57
|
@connect_timeout = connect_timeout
|
|
55
58
|
@read_timeout = read_timeout
|
|
56
59
|
@write_timeout = write_timeout
|
|
60
|
+
@idle_timeout = idle_timeout
|
|
57
61
|
|
|
58
62
|
@driver = driver ? RedisClient.driver(driver) : RedisClient.default_driver
|
|
59
63
|
|
|
@@ -84,6 +88,8 @@ class RedisClient
|
|
|
84
88
|
end
|
|
85
89
|
end
|
|
86
90
|
@middlewares_stack = middlewares_stack
|
|
91
|
+
|
|
92
|
+
@driver_info = driver_info
|
|
87
93
|
end
|
|
88
94
|
|
|
89
95
|
def connection_prelude
|
|
@@ -107,6 +113,13 @@ class RedisClient
|
|
|
107
113
|
prelude << ["SELECT", @db.to_s]
|
|
108
114
|
end
|
|
109
115
|
|
|
116
|
+
# Add CLIENT SETINFO commands for lib-name and lib-ver
|
|
117
|
+
# These commands are supported in Redis 7.2+
|
|
118
|
+
if @driver_info
|
|
119
|
+
prelude << ["CLIENT", "SETINFO", "LIB-NAME", build_lib_name]
|
|
120
|
+
prelude << ["CLIENT", "SETINFO", "LIB-VER", RedisClient::VERSION]
|
|
121
|
+
end
|
|
122
|
+
|
|
110
123
|
# Deep freeze all the strings and commands
|
|
111
124
|
prelude.map! do |commands|
|
|
112
125
|
commands = commands.map { |str| str.frozen? ? str : str.dup.freeze }
|
|
@@ -123,6 +136,25 @@ class RedisClient
|
|
|
123
136
|
@username || DEFAULT_USERNAME
|
|
124
137
|
end
|
|
125
138
|
|
|
139
|
+
# Build the library name for CLIENT SETINFO LIB-NAME.
|
|
140
|
+
#
|
|
141
|
+
# @param driver_info [String, Array<String>, nil] Upstream driver info
|
|
142
|
+
# @return [String] Library name with optional upstream driver info
|
|
143
|
+
# @raise [ArgumentError] if driver_info is not a String or Array
|
|
144
|
+
def build_lib_name
|
|
145
|
+
return "redis-client" if @driver_info.nil?
|
|
146
|
+
|
|
147
|
+
info = case @driver_info
|
|
148
|
+
when String then @driver_info
|
|
149
|
+
when Array then @driver_info.join(";")
|
|
150
|
+
else raise ArgumentError, "driver_info must be a String or Array of Strings"
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
return "redis-client" if info.empty?
|
|
154
|
+
|
|
155
|
+
"redis-client(#{info})"
|
|
156
|
+
end
|
|
157
|
+
|
|
126
158
|
def resolved?
|
|
127
159
|
true
|
|
128
160
|
end
|
|
@@ -47,7 +47,7 @@ class RedisClient
|
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def call_pipelined(commands, timeouts, exception: true)
|
|
50
|
-
|
|
50
|
+
first_error = last_error = nil
|
|
51
51
|
|
|
52
52
|
size = commands.size
|
|
53
53
|
results = Array.new(commands.size)
|
|
@@ -69,14 +69,16 @@ class RedisClient
|
|
|
69
69
|
result._set_command(commands[index])
|
|
70
70
|
result._set_config(config)
|
|
71
71
|
result._set_retry_attempt(@retry_attempt)
|
|
72
|
-
|
|
72
|
+
|
|
73
|
+
last_error&._set_next_error(result)
|
|
74
|
+
first_error ||= last_error = result
|
|
73
75
|
end
|
|
74
76
|
|
|
75
77
|
results[index] = result
|
|
76
78
|
end
|
|
77
79
|
|
|
78
|
-
if
|
|
79
|
-
raise
|
|
80
|
+
if first_error && exception
|
|
81
|
+
raise first_error
|
|
80
82
|
else
|
|
81
83
|
results
|
|
82
84
|
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'zlib'
|
|
4
|
+
|
|
5
|
+
class RedisClient
|
|
6
|
+
class HashRing
|
|
7
|
+
POINTS_PER_SERVER = 160
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
attr_writer :digest
|
|
11
|
+
|
|
12
|
+
def digest
|
|
13
|
+
@digest ||= begin
|
|
14
|
+
require 'digest/md5'
|
|
15
|
+
Digest::MD5
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
attr_reader :nodes
|
|
21
|
+
|
|
22
|
+
def initialize(nodes = [], replicas: POINTS_PER_SERVER, digest: self.class.digest)
|
|
23
|
+
@replicas = replicas
|
|
24
|
+
@ring = {}
|
|
25
|
+
@digest = digest
|
|
26
|
+
ids = {}
|
|
27
|
+
@nodes = nodes.dup.freeze
|
|
28
|
+
nodes.each do |node|
|
|
29
|
+
id = node.id || node.config.server_url
|
|
30
|
+
if ids[id]
|
|
31
|
+
raise ArgumentError, "duplicate node id: #{id.inspect}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
ids[id] = true
|
|
35
|
+
|
|
36
|
+
replicas.times do |i|
|
|
37
|
+
@ring[server_hash_for("#{id}:#{i}".freeze)] = node
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
@sorted_keys = @ring.keys
|
|
41
|
+
@sorted_keys.sort!
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# get the node in the hash ring for this key
|
|
45
|
+
def node_for(key)
|
|
46
|
+
hash = hash_for(key)
|
|
47
|
+
idx = binary_search(@sorted_keys, hash)
|
|
48
|
+
@ring[@sorted_keys[idx]]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def nodes_for(*keys)
|
|
52
|
+
keys.flatten!
|
|
53
|
+
mapping = {}
|
|
54
|
+
keys.each do |key|
|
|
55
|
+
(mapping[node_for(key)] ||= []) << key
|
|
56
|
+
end
|
|
57
|
+
mapping
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def hash_for(key)
|
|
63
|
+
Zlib.crc32(key)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def server_hash_for(key)
|
|
67
|
+
@digest.digest(key).unpack1("L>")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Find the closest index in HashRing with value <= the given value
|
|
71
|
+
def binary_search(ary, value)
|
|
72
|
+
upper = ary.size
|
|
73
|
+
lower = 0
|
|
74
|
+
|
|
75
|
+
while lower < upper
|
|
76
|
+
mid = (lower + upper) / 2
|
|
77
|
+
if ary[mid] > value
|
|
78
|
+
upper = mid
|
|
79
|
+
else
|
|
80
|
+
lower = mid + 1
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
upper - 1
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -116,6 +116,10 @@ class RedisClient
|
|
|
116
116
|
end
|
|
117
117
|
|
|
118
118
|
def check_role!(role)
|
|
119
|
+
unless role.is_a?(String)
|
|
120
|
+
raise TypeError, "Expected role to be a string, got: #{role.inspect}"
|
|
121
|
+
end
|
|
122
|
+
|
|
119
123
|
if @role == :master
|
|
120
124
|
unless role == "master"
|
|
121
125
|
sleep SENTINEL_DELAY
|
data/lib/redis_client/version.rb
CHANGED
data/lib/redis_client.rb
CHANGED
|
@@ -61,7 +61,7 @@ class RedisClient
|
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
module Common
|
|
64
|
-
attr_reader :config, :id
|
|
64
|
+
attr_reader :config, :id, :nodes
|
|
65
65
|
attr_accessor :connect_timeout, :read_timeout, :write_timeout
|
|
66
66
|
|
|
67
67
|
def initialize(
|
|
@@ -78,11 +78,21 @@ class RedisClient
|
|
|
78
78
|
@write_timeout = write_timeout
|
|
79
79
|
@command_builder = config.command_builder
|
|
80
80
|
@pid = PIDCache.pid
|
|
81
|
+
@nodes = [self].freeze
|
|
81
82
|
end
|
|
82
83
|
|
|
83
84
|
def timeout=(timeout)
|
|
84
85
|
@connect_timeout = @read_timeout = @write_timeout = timeout
|
|
85
86
|
end
|
|
87
|
+
|
|
88
|
+
def node_for(_key)
|
|
89
|
+
self
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def nodes_for(*keys)
|
|
93
|
+
keys.flatten!
|
|
94
|
+
{ self => keys }
|
|
95
|
+
end
|
|
86
96
|
end
|
|
87
97
|
|
|
88
98
|
module HasConfig
|
|
@@ -138,11 +148,17 @@ class RedisClient
|
|
|
138
148
|
include HasConfig
|
|
139
149
|
include Retriable
|
|
140
150
|
|
|
151
|
+
attr_reader :next_error
|
|
152
|
+
|
|
141
153
|
def self.with_config(message, config = nil)
|
|
142
154
|
error = new(message)
|
|
143
155
|
error._set_config(config)
|
|
144
156
|
error
|
|
145
157
|
end
|
|
158
|
+
|
|
159
|
+
def _set_next_error(error) # :nodoc:
|
|
160
|
+
@next_error = error
|
|
161
|
+
end
|
|
146
162
|
end
|
|
147
163
|
|
|
148
164
|
ProtocolError = Class.new(Error)
|
|
@@ -229,6 +245,12 @@ class RedisClient
|
|
|
229
245
|
SentinelConfig.new(client_implementation: self, **kwargs)
|
|
230
246
|
end
|
|
231
247
|
|
|
248
|
+
def ring(*clients, **options)
|
|
249
|
+
clients.flatten!
|
|
250
|
+
require "redis_client/hash_ring" unless defined?(HashRing)
|
|
251
|
+
HashRing.new(clients, **options)
|
|
252
|
+
end
|
|
253
|
+
|
|
232
254
|
def new(arg = nil, **kwargs)
|
|
233
255
|
if arg.is_a?(Config::Common)
|
|
234
256
|
super
|
|
@@ -269,6 +291,10 @@ class RedisClient
|
|
|
269
291
|
config.read_timeout
|
|
270
292
|
end
|
|
271
293
|
|
|
294
|
+
def idle_timeout
|
|
295
|
+
config.idle_timeout
|
|
296
|
+
end
|
|
297
|
+
|
|
272
298
|
def db
|
|
273
299
|
config.db
|
|
274
300
|
end
|
|
@@ -758,7 +784,9 @@ class RedisClient
|
|
|
758
784
|
@retry_attempt = config.retriable?(tries) ? tries : nil
|
|
759
785
|
connection = raw_connection
|
|
760
786
|
if block_given?
|
|
761
|
-
yield connection
|
|
787
|
+
result = yield connection
|
|
788
|
+
@last_used_at = RedisClient.now
|
|
789
|
+
result
|
|
762
790
|
else
|
|
763
791
|
connection
|
|
764
792
|
end
|
|
@@ -785,7 +813,9 @@ class RedisClient
|
|
|
785
813
|
begin
|
|
786
814
|
@disable_reconnection = true
|
|
787
815
|
@raw_connection.retry_attempt = nil
|
|
788
|
-
yield connection
|
|
816
|
+
result = yield connection
|
|
817
|
+
@last_used_at = RedisClient.now
|
|
818
|
+
result
|
|
789
819
|
rescue ConnectionError, ProtocolError
|
|
790
820
|
close
|
|
791
821
|
raise
|
|
@@ -799,6 +829,16 @@ class RedisClient
|
|
|
799
829
|
if @raw_connection.nil? || !@raw_connection.revalidate
|
|
800
830
|
connect
|
|
801
831
|
end
|
|
832
|
+
|
|
833
|
+
if config.idle_timeout && (@last_used_at + config.idle_timeout) < RedisClient.now
|
|
834
|
+
@middlewares.call(["PING"], config) do
|
|
835
|
+
@raw_connection.call(["PING"], nil)
|
|
836
|
+
rescue ConnectionError, ProtocolError
|
|
837
|
+
close
|
|
838
|
+
connect
|
|
839
|
+
end
|
|
840
|
+
end
|
|
841
|
+
|
|
802
842
|
@raw_connection.retry_attempt = @retry_attempt
|
|
803
843
|
@raw_connection
|
|
804
844
|
end
|
|
@@ -820,6 +860,7 @@ class RedisClient
|
|
|
820
860
|
)
|
|
821
861
|
end
|
|
822
862
|
end
|
|
863
|
+
@last_used_at = RedisClient.now
|
|
823
864
|
@raw_connection.retry_attempt = @retry_attempt
|
|
824
865
|
|
|
825
866
|
prelude = config.connection_prelude.dup
|
|
@@ -828,19 +869,26 @@ class RedisClient
|
|
|
828
869
|
prelude << ["CLIENT", "SETNAME", id]
|
|
829
870
|
end
|
|
830
871
|
|
|
831
|
-
# The connection prelude is deliberately not sent to Middlewares
|
|
832
872
|
if config.sentinel?
|
|
833
873
|
prelude << ["ROLE"]
|
|
834
|
-
|
|
835
|
-
|
|
874
|
+
end
|
|
875
|
+
|
|
876
|
+
unless prelude.empty?
|
|
877
|
+
results = @middlewares.call_pipelined(prelude, config) do
|
|
878
|
+
@raw_connection.call_pipelined(prelude, nil, exception: false)
|
|
836
879
|
end
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
880
|
+
|
|
881
|
+
results.each do |result|
|
|
882
|
+
# CLIENT SETINFO is unsupported on Redis < 7.2. The pipeline drained all responses before raising,
|
|
883
|
+
# so if that's the only error, the socket is healthy: keep it.
|
|
884
|
+
if result.is_a?(CommandError) && !(result.command[0] == "CLIENT" && result.command[1] == "SETINFO")
|
|
885
|
+
raise result
|
|
842
886
|
end
|
|
843
887
|
end
|
|
888
|
+
|
|
889
|
+
if config.sentinel?
|
|
890
|
+
config.check_role!(results.last.first)
|
|
891
|
+
end
|
|
844
892
|
end
|
|
845
893
|
rescue FailoverError, CannotConnectError => error
|
|
846
894
|
@raw_connection&.close
|
|
@@ -853,9 +901,10 @@ class RedisClient
|
|
|
853
901
|
raise connect_error
|
|
854
902
|
rescue CommandError => error
|
|
855
903
|
@raw_connection&.close
|
|
856
|
-
|
|
904
|
+
|
|
905
|
+
if error.command&.first == "HELLO" && error.message.match?(/ERR unknown command/)
|
|
857
906
|
raise UnsupportedServer,
|
|
858
|
-
"redis-client requires Redis 6+ with HELLO command available (#{config.server_url})"
|
|
907
|
+
"redis-client requires Redis 6+ with HELLO command available (#{config.server_url})", cause: error
|
|
859
908
|
else
|
|
860
909
|
raise
|
|
861
910
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: redis-client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.29.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jean Boussier
|
|
@@ -39,6 +39,7 @@ files:
|
|
|
39
39
|
- lib/redis_client/config.rb
|
|
40
40
|
- lib/redis_client/connection_mixin.rb
|
|
41
41
|
- lib/redis_client/decorator.rb
|
|
42
|
+
- lib/redis_client/hash_ring.rb
|
|
42
43
|
- lib/redis_client/middlewares.rb
|
|
43
44
|
- lib/redis_client/pid_cache.rb
|
|
44
45
|
- lib/redis_client/pooled.rb
|
|
@@ -70,7 +71,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
70
71
|
- !ruby/object:Gem::Version
|
|
71
72
|
version: '0'
|
|
72
73
|
requirements: []
|
|
73
|
-
rubygems_version: 4.0.
|
|
74
|
+
rubygems_version: 4.0.6
|
|
74
75
|
specification_version: 4
|
|
75
76
|
summary: Simple low-level client for Redis 6+
|
|
76
77
|
test_files: []
|