redis-client 0.26.2 → 0.28.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 +20 -0
- data/README.md +26 -0
- data/lib/redis_client/config.rb +34 -2
- data/lib/redis_client/hash_ring.rb +87 -0
- data/lib/redis_client/ruby_connection.rb +1 -1
- data/lib/redis_client/sentinel_config.rb +13 -5
- data/lib/redis_client/version.rb +1 -1
- data/lib/redis_client.rb +44 -3
- 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: 2fc78ecaeaed568c9917391585a7a185c90f8d7d569b8443c0fb2f0e918c2ca6
|
|
4
|
+
data.tar.gz: f9c7cbcb9c39267853681e926a71c27c3619bf0683fa5cd86885c585f2dfa239
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3738437f5876f4f8c09f82a19e4e75462779dae006ed37ac06d3d0ca997bb0190fc676b705e5f6186a5854caff4d98dddc8b035c0fbaa854f9d77b179cb01c94
|
|
7
|
+
data.tar.gz: 4f9788a5547647cee34ddf01f60400e9645ca2c73f5329155b58670019733ec945331674f94b5a7720754ea2dd12b85d62040060166145beaea9b462514e6500
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Unreleased
|
|
2
2
|
|
|
3
|
+
# 0.28.0
|
|
4
|
+
|
|
5
|
+
- Added `RedisClient::HashRing` for horizontal sharing (compatible with `Redis::Distributed` from `redis-rb`).
|
|
6
|
+
|
|
7
|
+
# 0.27.0
|
|
8
|
+
|
|
9
|
+
- Added `idle_timeout` to revalidate connections that haven't been successfuly used in a long time. Defaults to 30 seconds.
|
|
10
|
+
- Added `driver_info` configuration, to issue `CLIENT SETINFO` during connection prelude.
|
|
11
|
+
|
|
12
|
+
# 0.26.4
|
|
13
|
+
|
|
14
|
+
- Further improve `rediss://` URLs used with Redis sentinel. Now avoid override explictly set `ssl:` parameter.
|
|
15
|
+
- Fix compatibility with `redis-rb` in sentinel mode.
|
|
16
|
+
|
|
17
|
+
# 0.26.3
|
|
18
|
+
|
|
19
|
+
- Fix `rediss://` (ssl) URLs used with Redis sentinel.
|
|
20
|
+
- Handle Ruby 4.0 connection timeout raising an `IO::Timeout` instead of `Errno::ETIMEDOUT`.
|
|
21
|
+
- Entirely close the connection on authentication failures.
|
|
22
|
+
|
|
3
23
|
# 0.26.2
|
|
4
24
|
|
|
5
25
|
- Fix compatibility with `connection_pool` version 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,31 @@ 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
|
+
|
|
148
|
+
### Consistent Hashing
|
|
149
|
+
|
|
150
|
+
To horizontally shard keys across multiple servers without relying on clustering, a `RedisClient::HashRing` class is provided:
|
|
151
|
+
|
|
152
|
+
```ruby
|
|
153
|
+
ring = RedisClient.ring(
|
|
154
|
+
RedisClient.config(host: "10.0.1.1", port: 6380).new_pool(timeout: 0.5, size: 3),
|
|
155
|
+
RedisClient.config(host: "10.0.1.2", port: 6380).new_pool(timeout: 0.5, size: 3),
|
|
156
|
+
RedisClient.config(host: "10.0.1.3", port: 6380).new_pool(timeout: 0.5, size: 3),
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
ring.node_for("cache_key").call("GET", "cache_key") # => "value"
|
|
160
|
+
|
|
161
|
+
ring.nodes_for("key1", "key2", "key3").each do |node, keys|
|
|
162
|
+
node.call("DEL", *keys)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
ring.nodes.each do |node|
|
|
166
|
+
node.close
|
|
167
|
+
end
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
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.
|
|
171
|
+
|
|
146
172
|
# Use 'mysecret' to authenticate against the mymaster instance, but skip authentication for the sentinels:
|
|
147
173
|
SENTINELS = [{ host: '127.0.0.1', port: 26380 },
|
|
148
174
|
{ host: '127.0.0.1', port: 26381 }]
|
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
|
|
@@ -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
|
|
@@ -156,7 +156,7 @@ class RedisClient
|
|
|
156
156
|
write_timeout: @write_timeout,
|
|
157
157
|
)
|
|
158
158
|
true
|
|
159
|
-
rescue SystemCallError, OpenSSL::SSL::SSLError, SocketError => error
|
|
159
|
+
rescue SystemCallError, IOError, OpenSSL::SSL::SSLError, SocketError => error
|
|
160
160
|
socket&.close
|
|
161
161
|
raise CannotConnectError, error.message, error.backtrace
|
|
162
162
|
end
|
|
@@ -22,12 +22,16 @@ class RedisClient
|
|
|
22
22
|
raise ArgumentError, "Expected role to be either :master or :replica, got: #{role.inspect}"
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
# Track whether SSL was explicitly provided by user
|
|
26
|
+
ssl_explicitly_set = client_config.key?(:ssl)
|
|
27
|
+
|
|
25
28
|
if url
|
|
26
29
|
url_config = URLConfig.new(url)
|
|
27
30
|
client_config = {
|
|
28
31
|
username: url_config.username,
|
|
29
32
|
password: url_config.password,
|
|
30
33
|
db: url_config.db,
|
|
34
|
+
ssl: url_config.ssl?,
|
|
31
35
|
}.compact.merge(client_config)
|
|
32
36
|
name ||= url_config.host
|
|
33
37
|
end
|
|
@@ -66,6 +70,10 @@ class RedisClient
|
|
|
66
70
|
|
|
67
71
|
client_config[:reconnect_attempts] ||= DEFAULT_RECONNECT_ATTEMPTS
|
|
68
72
|
@client_config = client_config || {}
|
|
73
|
+
@sentinel_client_config = @client_config.dup
|
|
74
|
+
# Only remove SSL from sentinel config if it was derived from URL,
|
|
75
|
+
# not if explicitly set by user.
|
|
76
|
+
@sentinel_client_config.delete(:ssl) unless ssl_explicitly_set
|
|
69
77
|
super(**client_config)
|
|
70
78
|
@sentinel_configs = sentinels_to_configs(sentinels)
|
|
71
79
|
end
|
|
@@ -133,9 +141,9 @@ class RedisClient
|
|
|
133
141
|
sentinels.map do |sentinel|
|
|
134
142
|
case sentinel
|
|
135
143
|
when String
|
|
136
|
-
Config.new(**@
|
|
144
|
+
Config.new(**@sentinel_client_config, **@extra_config, url: sentinel)
|
|
137
145
|
else
|
|
138
|
-
Config.new(**@
|
|
146
|
+
Config.new(**@sentinel_client_config, **@extra_config, **sentinel)
|
|
139
147
|
end
|
|
140
148
|
end
|
|
141
149
|
end
|
|
@@ -152,7 +160,7 @@ class RedisClient
|
|
|
152
160
|
|
|
153
161
|
def resolve_master
|
|
154
162
|
each_sentinel do |sentinel_client|
|
|
155
|
-
host, port = sentinel_client.
|
|
163
|
+
host, port = sentinel_client.call_v(["SENTINEL", "get-master-addr-by-name", @name])
|
|
156
164
|
next unless host && port
|
|
157
165
|
|
|
158
166
|
refresh_sentinels(sentinel_client)
|
|
@@ -171,7 +179,7 @@ class RedisClient
|
|
|
171
179
|
|
|
172
180
|
def resolve_replica
|
|
173
181
|
each_sentinel do |sentinel_client|
|
|
174
|
-
replicas = sentinel_client.
|
|
182
|
+
replicas = sentinel_client.call_v(["SENTINEL", "replicas", @name], &@to_list_of_hash)
|
|
175
183
|
replicas.reject! do |r|
|
|
176
184
|
flags = r["flags"].to_s.split(",")
|
|
177
185
|
flags.include?("s_down") || flags.include?("o_down")
|
|
@@ -214,7 +222,7 @@ class RedisClient
|
|
|
214
222
|
end
|
|
215
223
|
|
|
216
224
|
def refresh_sentinels(sentinel_client)
|
|
217
|
-
sentinel_response = sentinel_client.
|
|
225
|
+
sentinel_response = sentinel_client.call_v(["SENTINEL", "sentinels", @name], &@to_list_of_hash)
|
|
218
226
|
sentinels = sentinel_response.map do |sentinel|
|
|
219
227
|
{ host: sentinel.fetch("ip"), port: Integer(sentinel.fetch("port")) }
|
|
220
228
|
end
|
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
|
|
@@ -229,6 +239,12 @@ class RedisClient
|
|
|
229
239
|
SentinelConfig.new(client_implementation: self, **kwargs)
|
|
230
240
|
end
|
|
231
241
|
|
|
242
|
+
def ring(*clients, **options)
|
|
243
|
+
clients.flatten!
|
|
244
|
+
require "redis_client/hash_ring" unless defined?(HashRing)
|
|
245
|
+
HashRing.new(clients, **options)
|
|
246
|
+
end
|
|
247
|
+
|
|
232
248
|
def new(arg = nil, **kwargs)
|
|
233
249
|
if arg.is_a?(Config::Common)
|
|
234
250
|
super
|
|
@@ -269,6 +285,10 @@ class RedisClient
|
|
|
269
285
|
config.read_timeout
|
|
270
286
|
end
|
|
271
287
|
|
|
288
|
+
def idle_timeout
|
|
289
|
+
config.idle_timeout
|
|
290
|
+
end
|
|
291
|
+
|
|
272
292
|
def db
|
|
273
293
|
config.db
|
|
274
294
|
end
|
|
@@ -758,7 +778,9 @@ class RedisClient
|
|
|
758
778
|
@retry_attempt = config.retriable?(tries) ? tries : nil
|
|
759
779
|
connection = raw_connection
|
|
760
780
|
if block_given?
|
|
761
|
-
yield connection
|
|
781
|
+
result = yield connection
|
|
782
|
+
@last_used_at = RedisClient.now
|
|
783
|
+
result
|
|
762
784
|
else
|
|
763
785
|
connection
|
|
764
786
|
end
|
|
@@ -785,7 +807,9 @@ class RedisClient
|
|
|
785
807
|
begin
|
|
786
808
|
@disable_reconnection = true
|
|
787
809
|
@raw_connection.retry_attempt = nil
|
|
788
|
-
yield connection
|
|
810
|
+
result = yield connection
|
|
811
|
+
@last_used_at = RedisClient.now
|
|
812
|
+
result
|
|
789
813
|
rescue ConnectionError, ProtocolError
|
|
790
814
|
close
|
|
791
815
|
raise
|
|
@@ -799,6 +823,16 @@ class RedisClient
|
|
|
799
823
|
if @raw_connection.nil? || !@raw_connection.revalidate
|
|
800
824
|
connect
|
|
801
825
|
end
|
|
826
|
+
|
|
827
|
+
if config.idle_timeout && (@last_used_at + config.idle_timeout) < RedisClient.now
|
|
828
|
+
@middlewares.call(["PING"], config) do
|
|
829
|
+
@raw_connection.call(["PING"], nil)
|
|
830
|
+
rescue ConnectionError, ProtocolError
|
|
831
|
+
close
|
|
832
|
+
connect
|
|
833
|
+
end
|
|
834
|
+
end
|
|
835
|
+
|
|
802
836
|
@raw_connection.retry_attempt = @retry_attempt
|
|
803
837
|
@raw_connection
|
|
804
838
|
end
|
|
@@ -820,6 +854,7 @@ class RedisClient
|
|
|
820
854
|
)
|
|
821
855
|
end
|
|
822
856
|
end
|
|
857
|
+
@last_used_at = RedisClient.now
|
|
823
858
|
@raw_connection.retry_attempt = @retry_attempt
|
|
824
859
|
|
|
825
860
|
prelude = config.connection_prelude.dup
|
|
@@ -843,16 +878,22 @@ class RedisClient
|
|
|
843
878
|
end
|
|
844
879
|
end
|
|
845
880
|
rescue FailoverError, CannotConnectError => error
|
|
881
|
+
@raw_connection&.close
|
|
846
882
|
error._set_config(config)
|
|
847
883
|
raise error
|
|
848
884
|
rescue ConnectionError => error
|
|
885
|
+
@raw_connection&.close
|
|
849
886
|
connect_error = CannotConnectError.with_config(error.message, config)
|
|
850
887
|
connect_error.set_backtrace(error.backtrace)
|
|
851
888
|
raise connect_error
|
|
852
889
|
rescue CommandError => error
|
|
890
|
+
@raw_connection&.close
|
|
853
891
|
if error.message.match?(/ERR unknown command ['`]HELLO['`]/)
|
|
854
892
|
raise UnsupportedServer,
|
|
855
893
|
"redis-client requires Redis 6+ with HELLO command available (#{config.server_url})"
|
|
894
|
+
# Ignore CLIENT SETINFO errors (Redis < 7.2 doesn't support it)
|
|
895
|
+
elsif error.message.match?(/unknown subcommand.*setinfo/i)
|
|
896
|
+
# Silently ignore - CLIENT SETINFO is not supported on Redis < 7.2
|
|
856
897
|
else
|
|
857
898
|
raise
|
|
858
899
|
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.28.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:
|
|
74
|
+
rubygems_version: 4.0.3
|
|
74
75
|
specification_version: 4
|
|
75
76
|
summary: Simple low-level client for Redis 6+
|
|
76
77
|
test_files: []
|