redis-client 0.25.3 → 0.26.1
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 +18 -1
- data/README.md +23 -0
- data/lib/redis_client/config.rb +10 -3
- data/lib/redis_client/connection_mixin.rb +22 -2
- data/lib/redis_client/ruby_connection.rb +11 -9
- data/lib/redis_client/sentinel_config.rb +4 -0
- data/lib/redis_client/version.rb +1 -1
- data/lib/redis_client.rb +47 -4
- 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: 532a6809e653318f469cf8aa5d3ed07498dd15b0557cea4449acde840f1c45ab
|
4
|
+
data.tar.gz: 8fef3135440099474344a957096db4ec1cc0d058e7e7ed80227282b37752ffd1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9a13cf5d2bed374fec9ad8d4cf92e2c3dda1b4aea9ea03180144b83ac7115daf4e5252c5c78b3dfbc4cf69eba7c0a79d5cc021a608d544d982c7165e2d77f18
|
7
|
+
data.tar.gz: 21c1b9828143c1edaba52f5359f15ac887ef8070fe3948ec893a63ad3800ba4612379985673beaa39dc6143fa0c39f18f7e1dab20beded463b748c752d8627aa
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 0.26.1
|
4
|
+
|
5
|
+
- Fix a few corner cases where `RedisClient::Error#final?` was innacurate.
|
6
|
+
- hiredis-client: Properly reconnect to the new leader after a sentinel failover.
|
7
|
+
|
8
|
+
# 0.26.0
|
9
|
+
|
10
|
+
- Add `RedisClient::Error#final?` and `#retriable?` to allow middleware to filter out non-final errors.
|
11
|
+
- Fix precedence of `db: nil` initialization parameter.
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
Redis.new(url: "redis://localhost:6379/3", db: nil).db
|
15
|
+
```
|
16
|
+
|
17
|
+
Before: `0`
|
18
|
+
After: `3`
|
19
|
+
|
3
20
|
# 0.25.3
|
4
21
|
|
5
22
|
- Fix `hiredis-client` compilation with `clang 21`.
|
@@ -205,7 +222,7 @@
|
|
205
222
|
- Added `_v` versions of `call` methods to make it easier to pass commands as arrays without splating.
|
206
223
|
- Fix calling `blocking_call` with a block in a pipeline.
|
207
224
|
- `blocking_call` now raise `ReadTimeoutError` if the command didn't complete in time.
|
208
|
-
- Fix `blocking_call` to not respect `
|
225
|
+
- Fix `blocking_call` to not respect `reconnect_attempts` on timeout.
|
209
226
|
- Stop parsing RESP3 sets as Ruby Set instances.
|
210
227
|
- Fix `SystemStackError` when parsing very large hashes. Fix: #30
|
211
228
|
- `hiredis` now more properly release the GVL when doing IOs.
|
data/README.md
CHANGED
@@ -459,6 +459,29 @@ RedisClient.register(MyGlobalRedisInstrumentation)
|
|
459
459
|
|
460
460
|
redis_config = RedisClient.config(custom: { tags: { "environment": Rails.env }})
|
461
461
|
```
|
462
|
+
|
463
|
+
### Instrumenting Errors
|
464
|
+
|
465
|
+
It is important to note that when `reconnect_attempts` is enabled, all network errors are reported to the middlewares,
|
466
|
+
even the ones that will be retried.
|
467
|
+
|
468
|
+
In many cases you may want to ignore retriable errors, or report them differently:
|
469
|
+
|
470
|
+
```ruby
|
471
|
+
module MyGlobalRedisInstrumentation
|
472
|
+
def call(command, redis_config)
|
473
|
+
super
|
474
|
+
rescue RedisClient::Error => error
|
475
|
+
if error.final?
|
476
|
+
# Error won't be retried.
|
477
|
+
else
|
478
|
+
# Error will be retried.
|
479
|
+
end
|
480
|
+
raise
|
481
|
+
end
|
482
|
+
end
|
483
|
+
```
|
484
|
+
|
462
485
|
### Timeouts
|
463
486
|
|
464
487
|
The client allows you to configure connect, read, and write timeouts.
|
data/lib/redis_client/config.rb
CHANGED
@@ -140,6 +140,10 @@ class RedisClient
|
|
140
140
|
@client_implementation.new(self, **kwargs)
|
141
141
|
end
|
142
142
|
|
143
|
+
def retriable?(attempt)
|
144
|
+
@reconnect_attempts && @reconnect_attempts[attempt]
|
145
|
+
end
|
146
|
+
|
143
147
|
def retry_connecting?(attempt, _error)
|
144
148
|
if @reconnect_attempts
|
145
149
|
if (pause = @reconnect_attempts[attempt])
|
@@ -182,7 +186,7 @@ class RedisClient
|
|
182
186
|
|
183
187
|
include Common
|
184
188
|
|
185
|
-
attr_reader :host, :port, :path
|
189
|
+
attr_reader :host, :port, :path, :server_key
|
186
190
|
|
187
191
|
def initialize(
|
188
192
|
url: nil,
|
@@ -191,14 +195,15 @@ class RedisClient
|
|
191
195
|
path: nil,
|
192
196
|
username: nil,
|
193
197
|
password: nil,
|
198
|
+
db: nil,
|
194
199
|
**kwargs
|
195
200
|
)
|
196
201
|
if url
|
197
202
|
url_config = URLConfig.new(url)
|
198
203
|
kwargs = {
|
199
204
|
ssl: url_config.ssl?,
|
200
|
-
db: url_config.db,
|
201
205
|
}.compact.merge(kwargs)
|
206
|
+
db ||= url_config.db
|
202
207
|
host ||= url_config.host
|
203
208
|
port ||= url_config.port
|
204
209
|
path ||= url_config.path
|
@@ -206,7 +211,7 @@ class RedisClient
|
|
206
211
|
password ||= url_config.password
|
207
212
|
end
|
208
213
|
|
209
|
-
super(username: username, password: password, **kwargs)
|
214
|
+
super(username: username, password: password, db: db, **kwargs)
|
210
215
|
|
211
216
|
if @path = path
|
212
217
|
@host = nil
|
@@ -215,6 +220,8 @@ class RedisClient
|
|
215
220
|
@host = host || DEFAULT_HOST
|
216
221
|
@port = Integer(port || DEFAULT_PORT)
|
217
222
|
end
|
223
|
+
|
224
|
+
@server_key = [@path, @host, @port].freeze
|
218
225
|
end
|
219
226
|
end
|
220
227
|
end
|
@@ -2,8 +2,14 @@
|
|
2
2
|
|
3
3
|
class RedisClient
|
4
4
|
module ConnectionMixin
|
5
|
-
|
5
|
+
attr_accessor :retry_attempt
|
6
|
+
attr_reader :config
|
7
|
+
|
8
|
+
def initialize(config)
|
6
9
|
@pending_reads = 0
|
10
|
+
@retry_attempt = nil
|
11
|
+
@config = config
|
12
|
+
@server_key = nil
|
7
13
|
end
|
8
14
|
|
9
15
|
def reconnect
|
@@ -17,7 +23,7 @@ class RedisClient
|
|
17
23
|
end
|
18
24
|
|
19
25
|
def revalidate
|
20
|
-
if @pending_reads > 0
|
26
|
+
if @pending_reads > 0 || @server_key != @config.server_key
|
21
27
|
close
|
22
28
|
false
|
23
29
|
else
|
@@ -33,6 +39,7 @@ class RedisClient
|
|
33
39
|
if result.is_a?(Error)
|
34
40
|
result._set_command(command)
|
35
41
|
result._set_config(config)
|
42
|
+
result._set_retry_attempt(@retry_attempt)
|
36
43
|
raise result
|
37
44
|
else
|
38
45
|
result
|
@@ -61,6 +68,7 @@ class RedisClient
|
|
61
68
|
elsif result.is_a?(Error)
|
62
69
|
result._set_command(commands[index])
|
63
70
|
result._set_config(config)
|
71
|
+
result._set_retry_attempt(@retry_attempt)
|
64
72
|
first_exception ||= result
|
65
73
|
end
|
66
74
|
|
@@ -82,5 +90,17 @@ class RedisClient
|
|
82
90
|
# to account for the network delay.
|
83
91
|
timeout + config.read_timeout
|
84
92
|
end
|
93
|
+
|
94
|
+
def protocol_error(message)
|
95
|
+
error = ProtocolError.with_config(message, config)
|
96
|
+
error._set_retry_attempt(@retry_attempt)
|
97
|
+
error
|
98
|
+
end
|
99
|
+
|
100
|
+
def connection_error(message)
|
101
|
+
error = ConnectionError.with_config(message, config)
|
102
|
+
error._set_retry_attempt(@retry_attempt)
|
103
|
+
error
|
104
|
+
end
|
85
105
|
end
|
86
106
|
end
|
@@ -40,11 +40,8 @@ class RedisClient
|
|
40
40
|
|
41
41
|
SUPPORTS_RESOLV_TIMEOUT = Socket.method(:tcp).parameters.any? { |p| p.last == :resolv_timeout }
|
42
42
|
|
43
|
-
attr_reader :config
|
44
|
-
|
45
43
|
def initialize(config, connect_timeout:, read_timeout:, write_timeout:)
|
46
|
-
super()
|
47
|
-
@config = config
|
44
|
+
super(config)
|
48
45
|
@connect_timeout = connect_timeout
|
49
46
|
@read_timeout = read_timeout
|
50
47
|
@write_timeout = write_timeout
|
@@ -75,7 +72,11 @@ class RedisClient
|
|
75
72
|
begin
|
76
73
|
@io.write(buffer)
|
77
74
|
rescue SystemCallError, IOError, OpenSSL::SSL::SSLError => error
|
78
|
-
raise
|
75
|
+
raise connection_error(error.message)
|
76
|
+
rescue Error => error
|
77
|
+
error._set_config(config)
|
78
|
+
error._set_retry_attempt(@retry_attempt)
|
79
|
+
raise error
|
79
80
|
end
|
80
81
|
end
|
81
82
|
|
@@ -87,7 +88,7 @@ class RedisClient
|
|
87
88
|
begin
|
88
89
|
@io.write(buffer)
|
89
90
|
rescue SystemCallError, IOError, OpenSSL::SSL::SSLError => error
|
90
|
-
raise
|
91
|
+
raise connection_error(error.message)
|
91
92
|
end
|
92
93
|
end
|
93
94
|
|
@@ -97,10 +98,10 @@ class RedisClient
|
|
97
98
|
else
|
98
99
|
@io.with_timeout(timeout) { RESP3.load(@io) }
|
99
100
|
end
|
100
|
-
rescue RedisClient::RESP3::
|
101
|
-
raise
|
101
|
+
rescue RedisClient::RESP3::Error => error
|
102
|
+
raise protocol_error(error.message)
|
102
103
|
rescue SystemCallError, IOError, OpenSSL::SSL::SSLError => error
|
103
|
-
raise
|
104
|
+
raise connection_error(error.message)
|
104
105
|
end
|
105
106
|
|
106
107
|
def measure_round_trip_delay
|
@@ -112,6 +113,7 @@ class RedisClient
|
|
112
113
|
private
|
113
114
|
|
114
115
|
def connect
|
116
|
+
@server_key = @config.server_key
|
115
117
|
socket = if @config.path
|
116
118
|
UNIXSocket.new(@config.path)
|
117
119
|
else
|
data/lib/redis_client/version.rb
CHANGED
data/lib/redis_client.rb
CHANGED
@@ -99,13 +99,49 @@ class RedisClient
|
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
102
|
+
module Retriable
|
103
|
+
def _set_retry_attempt(retry_attempt)
|
104
|
+
@retry_attempt = retry_attempt
|
105
|
+
end
|
106
|
+
|
107
|
+
def retry_attempt
|
108
|
+
@retry_attempt || 0
|
109
|
+
end
|
110
|
+
|
111
|
+
def retriable?
|
112
|
+
!!@retry_attempt
|
113
|
+
end
|
114
|
+
|
115
|
+
def final?
|
116
|
+
!@retry_attempt
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
module Final
|
121
|
+
def _set_retry_attempt(_retry_attempt)
|
122
|
+
end
|
123
|
+
|
124
|
+
def retry_attempt
|
125
|
+
0
|
126
|
+
end
|
127
|
+
|
128
|
+
def retriable?
|
129
|
+
false
|
130
|
+
end
|
131
|
+
|
132
|
+
def final?
|
133
|
+
true
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
102
137
|
class Error < StandardError
|
103
138
|
include HasConfig
|
139
|
+
include Retriable
|
104
140
|
|
105
141
|
def self.with_config(message, config = nil)
|
106
|
-
new(message)
|
107
|
-
|
108
|
-
|
142
|
+
error = new(message)
|
143
|
+
error._set_config(config)
|
144
|
+
error
|
109
145
|
end
|
110
146
|
end
|
111
147
|
|
@@ -142,6 +178,7 @@ class RedisClient
|
|
142
178
|
class CommandError < Error
|
143
179
|
include HasCommand
|
144
180
|
include HasCode
|
181
|
+
include Final
|
145
182
|
|
146
183
|
class << self
|
147
184
|
def parse(error_message)
|
@@ -212,6 +249,7 @@ class RedisClient
|
|
212
249
|
@middlewares = config.middlewares_stack.new(self)
|
213
250
|
@raw_connection = nil
|
214
251
|
@disable_reconnection = false
|
252
|
+
@retry_attempt = nil
|
215
253
|
end
|
216
254
|
|
217
255
|
def inspect
|
@@ -706,6 +744,7 @@ class RedisClient
|
|
706
744
|
close if !config.inherit_socket && @pid != PIDCache.pid
|
707
745
|
|
708
746
|
if @disable_reconnection
|
747
|
+
@raw_connection.retry_attempt = nil
|
709
748
|
if block_given?
|
710
749
|
yield @raw_connection
|
711
750
|
else
|
@@ -716,6 +755,7 @@ class RedisClient
|
|
716
755
|
connection = nil
|
717
756
|
preferred_error = nil
|
718
757
|
begin
|
758
|
+
@retry_attempt = config.retriable?(tries) ? tries : nil
|
719
759
|
connection = raw_connection
|
720
760
|
if block_given?
|
721
761
|
yield connection
|
@@ -744,6 +784,7 @@ class RedisClient
|
|
744
784
|
connection = ensure_connected
|
745
785
|
begin
|
746
786
|
@disable_reconnection = true
|
787
|
+
@raw_connection.retry_attempt = nil
|
747
788
|
yield connection
|
748
789
|
rescue ConnectionError, ProtocolError
|
749
790
|
close
|
@@ -758,13 +799,14 @@ class RedisClient
|
|
758
799
|
if @raw_connection.nil? || !@raw_connection.revalidate
|
759
800
|
connect
|
760
801
|
end
|
802
|
+
@raw_connection.retry_attempt = @retry_attempt
|
761
803
|
@raw_connection
|
762
804
|
end
|
763
805
|
|
764
806
|
def connect
|
765
807
|
@pid = PIDCache.pid
|
766
808
|
|
767
|
-
if @raw_connection
|
809
|
+
if @raw_connection&.revalidate
|
768
810
|
@middlewares.connect(config) do
|
769
811
|
@raw_connection.reconnect
|
770
812
|
end
|
@@ -778,6 +820,7 @@ class RedisClient
|
|
778
820
|
)
|
779
821
|
end
|
780
822
|
end
|
823
|
+
@raw_connection.retry_attempt = @retry_attempt
|
781
824
|
|
782
825
|
prelude = config.connection_prelude.dup
|
783
826
|
|
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.26.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jean Boussier
|
@@ -70,7 +70,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
70
|
- !ruby/object:Gem::Version
|
71
71
|
version: '0'
|
72
72
|
requirements: []
|
73
|
-
rubygems_version: 3.
|
73
|
+
rubygems_version: 3.6.9
|
74
74
|
specification_version: 4
|
75
75
|
summary: Simple low-level client for Redis 6+
|
76
76
|
test_files: []
|