redis-client 0.28.0 → 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 +5 -0
- data/README.md +28 -24
- data/lib/redis_client/connection_mixin.rb +6 -4
- data/lib/redis_client/sentinel_config.rb +4 -0
- data/lib/redis_client/version.rb +1 -1
- data/lib/redis_client.rb +24 -13
- 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: 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,5 +1,10 @@
|
|
|
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
|
+
|
|
3
8
|
# 0.28.0
|
|
4
9
|
|
|
5
10
|
- Added `RedisClient::HashRing` for horizontal sharing (compatible with `Redis::Distributed` from `redis-rb`).
|
data/README.md
CHANGED
|
@@ -145,6 +145,29 @@ If you specify a username and/or password at the top level for your main Redis i
|
|
|
145
145
|
|
|
146
146
|
```ruby
|
|
147
147
|
|
|
148
|
+
# Use 'mysecret' to authenticate against the mymaster instance, but skip authentication for the sentinels:
|
|
149
|
+
SENTINELS = [{ host: '127.0.0.1', port: 26380 },
|
|
150
|
+
{ host: '127.0.0.1', port: 26381 }]
|
|
151
|
+
|
|
152
|
+
redis_config = RedisClient.sentinel(name: 'mymaster', sentinels: SENTINELS, role: :master, password: 'mysecret')
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
So you have to provide Sentinel credential and Redis explicitly even they are the same
|
|
156
|
+
|
|
157
|
+
```ruby
|
|
158
|
+
# Use 'mysecret' to authenticate against the mymaster instance and sentinel
|
|
159
|
+
SENTINELS = [{ host: '127.0.0.1', port: 26380 },
|
|
160
|
+
{ host: '127.0.0.1', port: 26381 }]
|
|
161
|
+
|
|
162
|
+
redis_config = RedisClient.sentinel(name: 'mymaster', sentinels: SENTINELS, role: :master, password: 'mysecret', sentinel_password: 'mysecret')
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Also the `name`, `password`, `username` and `db` for Redis instance can be passed as an url:
|
|
166
|
+
|
|
167
|
+
```ruby
|
|
168
|
+
redis_config = RedisClient.sentinel(url: "redis://appuser:mysecret@mymaster/10", sentinels: SENTINELS, role: :master)
|
|
169
|
+
```
|
|
170
|
+
|
|
148
171
|
### Consistent Hashing
|
|
149
172
|
|
|
150
173
|
To horizontally shard keys across multiple servers without relying on clustering, a `RedisClient::HashRing` class is provided:
|
|
@@ -169,29 +192,6 @@ end
|
|
|
169
192
|
|
|
170
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.
|
|
171
194
|
|
|
172
|
-
# Use 'mysecret' to authenticate against the mymaster instance, but skip authentication for the sentinels:
|
|
173
|
-
SENTINELS = [{ host: '127.0.0.1', port: 26380 },
|
|
174
|
-
{ host: '127.0.0.1', port: 26381 }]
|
|
175
|
-
|
|
176
|
-
redis_config = RedisClient.sentinel(name: 'mymaster', sentinels: SENTINELS, role: :master, password: 'mysecret')
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
So you have to provide Sentinel credential and Redis explicitly even they are the same
|
|
180
|
-
|
|
181
|
-
```ruby
|
|
182
|
-
# Use 'mysecret' to authenticate against the mymaster instance and sentinel
|
|
183
|
-
SENTINELS = [{ host: '127.0.0.1', port: 26380 },
|
|
184
|
-
{ host: '127.0.0.1', port: 26381 }]
|
|
185
|
-
|
|
186
|
-
redis_config = RedisClient.sentinel(name: 'mymaster', sentinels: SENTINELS, role: :master, password: 'mysecret', sentinel_password: 'mysecret')
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
Also the `name`, `password`, `username` and `db` for Redis instance can be passed as an url:
|
|
190
|
-
|
|
191
|
-
```ruby
|
|
192
|
-
redis_config = RedisClient.sentinel(url: "redis://appuser:mysecret@mymaster/10", sentinels: SENTINELS, role: :master)
|
|
193
|
-
```
|
|
194
|
-
|
|
195
195
|
### Type support
|
|
196
196
|
|
|
197
197
|
Only a select few Ruby types are supported as arguments beside strings.
|
|
@@ -361,7 +361,11 @@ end
|
|
|
361
361
|
|
|
362
362
|
#### Exception management
|
|
363
363
|
|
|
364
|
-
|
|
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
|
|
365
369
|
behavior. When set to `false`, it doesn't raise an exception when a command error occurs. Instead, it allows the
|
|
366
370
|
pipeline to execute all commands, and any failed command will be available in the returned array. (Defaults to `true`)
|
|
367
371
|
|
|
@@ -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
|
|
@@ -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
|
@@ -148,11 +148,17 @@ class RedisClient
|
|
|
148
148
|
include HasConfig
|
|
149
149
|
include Retriable
|
|
150
150
|
|
|
151
|
+
attr_reader :next_error
|
|
152
|
+
|
|
151
153
|
def self.with_config(message, config = nil)
|
|
152
154
|
error = new(message)
|
|
153
155
|
error._set_config(config)
|
|
154
156
|
error
|
|
155
157
|
end
|
|
158
|
+
|
|
159
|
+
def _set_next_error(error) # :nodoc:
|
|
160
|
+
@next_error = error
|
|
161
|
+
end
|
|
156
162
|
end
|
|
157
163
|
|
|
158
164
|
ProtocolError = Class.new(Error)
|
|
@@ -863,19 +869,26 @@ class RedisClient
|
|
|
863
869
|
prelude << ["CLIENT", "SETNAME", id]
|
|
864
870
|
end
|
|
865
871
|
|
|
866
|
-
# The connection prelude is deliberately not sent to Middlewares
|
|
867
872
|
if config.sentinel?
|
|
868
873
|
prelude << ["ROLE"]
|
|
869
|
-
|
|
870
|
-
|
|
874
|
+
end
|
|
875
|
+
|
|
876
|
+
unless prelude.empty?
|
|
877
|
+
results = @middlewares.call_pipelined(prelude, config) do
|
|
878
|
+
@raw_connection.call_pipelined(prelude, nil, exception: false)
|
|
871
879
|
end
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
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
|
|
877
886
|
end
|
|
878
887
|
end
|
|
888
|
+
|
|
889
|
+
if config.sentinel?
|
|
890
|
+
config.check_role!(results.last.first)
|
|
891
|
+
end
|
|
879
892
|
end
|
|
880
893
|
rescue FailoverError, CannotConnectError => error
|
|
881
894
|
@raw_connection&.close
|
|
@@ -888,12 +901,10 @@ class RedisClient
|
|
|
888
901
|
raise connect_error
|
|
889
902
|
rescue CommandError => error
|
|
890
903
|
@raw_connection&.close
|
|
891
|
-
|
|
904
|
+
|
|
905
|
+
if error.command&.first == "HELLO" && error.message.match?(/ERR unknown command/)
|
|
892
906
|
raise UnsupportedServer,
|
|
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
|
|
907
|
+
"redis-client requires Redis 6+ with HELLO command available (#{config.server_url})", cause: error
|
|
897
908
|
else
|
|
898
909
|
raise
|
|
899
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
|
|
@@ -71,7 +71,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
71
71
|
- !ruby/object:Gem::Version
|
|
72
72
|
version: '0'
|
|
73
73
|
requirements: []
|
|
74
|
-
rubygems_version: 4.0.
|
|
74
|
+
rubygems_version: 4.0.6
|
|
75
75
|
specification_version: 4
|
|
76
76
|
summary: Simple low-level client for Redis 6+
|
|
77
77
|
test_files: []
|