redis-client 0.11.1 → 0.12.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/Gemfile.lock +3 -3
- data/README.md +47 -8
- data/lib/redis_client/circuit_breaker.rb +108 -0
- data/lib/redis_client/config.rb +12 -2
- data/lib/redis_client/connection_mixin.rb +2 -2
- data/lib/redis_client/ruby_connection/buffered_io.rb +1 -1
- data/lib/redis_client/version.rb +1 -1
- data/lib/redis_client.rb +22 -11
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 398676851a40081cd70d1334fa845292b0df4b7036d6ae33d0fa3ae6fa7ee040
|
|
4
|
+
data.tar.gz: 0a17e311c65180dcd67f106bcaca6e1f08a441e38e9ffdbab9876e2b3f62bc0a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 196f490ad7632f4139099b6047c3973b8957be2df24a92f1ba9f5f7588360c2a89b04b487cd24251e4aabf6006bd51cece7429f9aeae75b9e5013cb00240bdfc
|
|
7
|
+
data.tar.gz: bee6d01afc6805fa70720c09b213841d719a4bb045b6694d5a1d9c9efe6a5875c403464b7451d0d0a4d8056467440306936250cd6f537d47b45ee962dfa90658
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
# Unreleased
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# 0.12.0
|
|
4
|
+
|
|
5
|
+
- hiredis: fix a compilation issue on macOS and Ruby 3.2.0. See: #79
|
|
6
|
+
- Close connection on MASTERDOWN errors. Similar to READONLY.
|
|
7
|
+
- Add a `circuit_breaker` configuration option for cache servers and other disposable Redis servers. See #55 / #70
|
|
8
|
+
|
|
9
|
+
# 0.11.2
|
|
10
|
+
|
|
11
|
+
- Close connection on READONLY errors. Fix: #64
|
|
12
|
+
- Handle Redis 6+ servers with a missing HELLO command. See: #67
|
|
13
|
+
- Validate `url` parameters a bit more strictly. Fix #61
|
|
14
|
+
|
|
15
|
+
# 0.11.1
|
|
16
|
+
|
|
17
|
+
- hiredis: Workaround a compilation bug with Xcode 14.0. Fix: #58
|
|
4
18
|
- Accept `URI` instances as `uri` parameter.
|
|
5
19
|
|
|
6
20
|
# 0.11.0
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
redis-client (0.
|
|
4
|
+
redis-client (0.12.0)
|
|
5
5
|
connection_pool
|
|
6
6
|
|
|
7
7
|
GEM
|
|
@@ -19,7 +19,7 @@ GEM
|
|
|
19
19
|
ast (~> 2.4.1)
|
|
20
20
|
rainbow (3.1.1)
|
|
21
21
|
rake (13.0.6)
|
|
22
|
-
rake-compiler (1.2.
|
|
22
|
+
rake-compiler (1.2.1)
|
|
23
23
|
rake
|
|
24
24
|
redis (4.6.0)
|
|
25
25
|
regexp_parser (2.5.0)
|
|
@@ -38,7 +38,7 @@ GEM
|
|
|
38
38
|
rubocop-minitest (0.19.1)
|
|
39
39
|
rubocop (>= 0.90, < 2.0)
|
|
40
40
|
ruby-progressbar (1.11.0)
|
|
41
|
-
stackprof (0.2.
|
|
41
|
+
stackprof (0.2.23)
|
|
42
42
|
toxiproxy (2.0.2)
|
|
43
43
|
unicode-display_width (2.2.0)
|
|
44
44
|
|
data/README.md
CHANGED
|
@@ -63,7 +63,7 @@ redis.call("GET", "mykey")
|
|
|
63
63
|
### Configuration
|
|
64
64
|
|
|
65
65
|
- `url`: A Redis connection URL, e.g. `redis://example.com:6379/5`, a `rediss://` scheme enable SSL, and the path is interpreted as a database number.
|
|
66
|
-
Note that all other configurations take precedence, e.g. `RedisClient.config(url: "redis://localhost:3000" port: 6380)` will connect on port `6380`.
|
|
66
|
+
Note that all other configurations take precedence, e.g. `RedisClient.config(url: "redis://localhost:3000", port: 6380)` will connect on port `6380`.
|
|
67
67
|
- `host`: The server hostname or IP address. Defaults to `"localhost"`.
|
|
68
68
|
- `port`: The server port. Defaults to `6379`.
|
|
69
69
|
- `path`: The path to a UNIX socket, if set `url`, `host` and `port` are ignored.
|
|
@@ -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
|
+
- `circuit_breaker`: A Hash with circuit breaker configuration. Defaults to `nil`. See the [circuit breaker section](#circuit-breaker) for details.
|
|
84
85
|
- `protocol:` The version of the RESP protocol to use. Default to `3`.
|
|
85
86
|
- `custom`: A user owned value ignored by `redis-client` but available as `Config#custom`. This can be used to hold middleware configurations and other user specific metadatas.
|
|
86
87
|
|
|
@@ -426,24 +427,62 @@ It can be set as a number of retries:
|
|
|
426
427
|
redis_config = RedisClient.config(reconnect_attempts: 1)
|
|
427
428
|
```
|
|
428
429
|
|
|
429
|
-
Or as a list of sleep durations for implementing exponential backoff:
|
|
430
|
-
|
|
431
|
-
```ruby
|
|
432
|
-
redis_config = RedisClient.config(reconnect_attempts: [0, 0.05, 0.1])
|
|
433
|
-
```
|
|
434
|
-
|
|
435
430
|
**Important Note**: Retrying may cause commands to be issued more than once to the server, so in the case of
|
|
436
431
|
non-idempotent commands such as `LPUSH` or `INCR`, it may cause consistency issues.
|
|
437
432
|
|
|
438
433
|
To selectively disable automatic retries, you can use the `#call_once` method:
|
|
439
434
|
|
|
440
435
|
```ruby
|
|
441
|
-
redis_config = RedisClient.config(reconnect_attempts:
|
|
436
|
+
redis_config = RedisClient.config(reconnect_attempts: 3)
|
|
442
437
|
redis = redis_config.new_client
|
|
443
438
|
redis.call("GET", "counter") # Will be retried up to 3 times.
|
|
444
439
|
redis.call_once("INCR", "counter") # Won't be retried.
|
|
445
440
|
```
|
|
446
441
|
|
|
442
|
+
### Exponential backoff
|
|
443
|
+
|
|
444
|
+
Alternatively, `reconnect_attempts` accepts a list of sleep durations for implementing exponential backoff:
|
|
445
|
+
|
|
446
|
+
```ruby
|
|
447
|
+
redis_config = RedisClient.config(reconnect_attempts: [0, 0.05, 0.1])
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
This configuration is generally used when the Redis server is expected to failover or recover relatively quickly and
|
|
451
|
+
that it's not really possibe to continue without issuing the command.
|
|
452
|
+
|
|
453
|
+
When the Redis server is used as an ephemeral cache, circuit breakers are generally prefered.
|
|
454
|
+
|
|
455
|
+
### Circuit Breaker
|
|
456
|
+
|
|
457
|
+
When Redis is used as a cache and a connection error happens, you may not want to retry as it might take
|
|
458
|
+
longer than to recompute the value. Instead it's likely preferable to mark the server as unavailable and let it
|
|
459
|
+
recover for a while.
|
|
460
|
+
|
|
461
|
+
[Circuit breakers are a pattern that does exactly that](https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern).
|
|
462
|
+
|
|
463
|
+
Configuation options:
|
|
464
|
+
|
|
465
|
+
- `error_threshold`. The amount of errors to encounter within `error_threshold_timeout` amount of time before opening the circuit, that is to start rejecting requests instantly.
|
|
466
|
+
- `error_threshold_timeout`. The amount of time in seconds that `error_threshold` errors must occur to open the circuit. Defaults to `error_timeout` seconds if not set.
|
|
467
|
+
- `error_timeout`. The amount of time in seconds until trying to query the resource again.
|
|
468
|
+
- `success_threshold`. The amount of successes on the circuit until closing it again, that is to start accepting all requests to the circuit.
|
|
469
|
+
|
|
470
|
+
```ruby
|
|
471
|
+
RedisClient.config(
|
|
472
|
+
circuit_breaker: {
|
|
473
|
+
# Stop querying the server after 3 errors happened in a 2 seconds window
|
|
474
|
+
error_threshold: 3,
|
|
475
|
+
error_threshold_timeout: 2,
|
|
476
|
+
|
|
477
|
+
# Try querying again after 1 second
|
|
478
|
+
error_timeout: 1,
|
|
479
|
+
|
|
480
|
+
# Stay in half-open state until 3 queries succeeded.
|
|
481
|
+
success_threshold: 3,
|
|
482
|
+
}
|
|
483
|
+
)
|
|
484
|
+
```
|
|
485
|
+
|
|
447
486
|
### Drivers
|
|
448
487
|
|
|
449
488
|
`redis-client` ships with a pure Ruby socket implementation.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class RedisClient
|
|
4
|
+
class CircuitBreaker
|
|
5
|
+
module Middleware
|
|
6
|
+
def connect(config)
|
|
7
|
+
config.circuit_breaker.protect { super }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def call(_command, config)
|
|
11
|
+
config.circuit_breaker.protect { super }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call_pipelined(_commands, config)
|
|
15
|
+
config.circuit_breaker.protect { super }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
OpenCircuitError = Class.new(CannotConnectError)
|
|
20
|
+
|
|
21
|
+
attr_reader :error_timeout, :error_threshold, :error_threshold_timeout, :success_threshold
|
|
22
|
+
|
|
23
|
+
def initialize(error_threshold:, error_timeout:, error_threshold_timeout: error_timeout, success_threshold: 0)
|
|
24
|
+
@error_threshold = Integer(error_threshold)
|
|
25
|
+
@error_threshold_timeout = Float(error_threshold_timeout)
|
|
26
|
+
@error_timeout = Float(error_timeout)
|
|
27
|
+
@success_threshold = Integer(success_threshold)
|
|
28
|
+
@errors = []
|
|
29
|
+
@successes = 0
|
|
30
|
+
@state = :closed
|
|
31
|
+
@lock = Mutex.new
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def protect
|
|
35
|
+
if @state == :open
|
|
36
|
+
refresh_state
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
case @state
|
|
40
|
+
when :open
|
|
41
|
+
raise OpenCircuitError, "Too many connection errors happened recently"
|
|
42
|
+
when :closed
|
|
43
|
+
begin
|
|
44
|
+
yield
|
|
45
|
+
rescue ConnectionError
|
|
46
|
+
record_error
|
|
47
|
+
raise
|
|
48
|
+
end
|
|
49
|
+
when :half_open
|
|
50
|
+
begin
|
|
51
|
+
result = yield
|
|
52
|
+
record_success
|
|
53
|
+
result
|
|
54
|
+
rescue ConnectionError
|
|
55
|
+
record_error
|
|
56
|
+
raise
|
|
57
|
+
end
|
|
58
|
+
else
|
|
59
|
+
raise "[BUG] RedisClient::CircuitBreaker unexpected @state (#{@state.inspect}})"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def refresh_state
|
|
66
|
+
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
67
|
+
@lock.synchronize do
|
|
68
|
+
if @errors.last < (now - @error_timeout)
|
|
69
|
+
if @success_threshold > 0
|
|
70
|
+
@state = :half_open
|
|
71
|
+
@successes = 0
|
|
72
|
+
else
|
|
73
|
+
@errors.clear
|
|
74
|
+
@state = :closed
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def record_error
|
|
81
|
+
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
82
|
+
expiry = now - @error_timeout
|
|
83
|
+
@lock.synchronize do
|
|
84
|
+
if @state == :closed
|
|
85
|
+
@errors.reject! { |t| t < expiry }
|
|
86
|
+
end
|
|
87
|
+
@errors << now
|
|
88
|
+
@successes = 0
|
|
89
|
+
if @state == :half_open || (@state == :closed && @errors.size >= @error_threshold)
|
|
90
|
+
@state = :open
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def record_success
|
|
96
|
+
return unless @state == :half_open
|
|
97
|
+
|
|
98
|
+
@lock.synchronize do
|
|
99
|
+
return unless @state == :half_open
|
|
100
|
+
|
|
101
|
+
@successes += 1
|
|
102
|
+
if @successes >= @success_threshold
|
|
103
|
+
@state = :closed
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
data/lib/redis_client/config.rb
CHANGED
|
@@ -14,7 +14,7 @@ class RedisClient
|
|
|
14
14
|
module Common
|
|
15
15
|
attr_reader :db, :password, :id, :ssl, :ssl_params, :command_builder, :inherit_socket,
|
|
16
16
|
:connect_timeout, :read_timeout, :write_timeout, :driver, :connection_prelude, :protocol,
|
|
17
|
-
:middlewares_stack, :custom
|
|
17
|
+
:middlewares_stack, :custom, :circuit_breaker
|
|
18
18
|
|
|
19
19
|
alias_method :ssl?, :ssl
|
|
20
20
|
|
|
@@ -36,7 +36,8 @@ class RedisClient
|
|
|
36
36
|
command_builder: CommandBuilder,
|
|
37
37
|
inherit_socket: false,
|
|
38
38
|
reconnect_attempts: false,
|
|
39
|
-
middlewares: false
|
|
39
|
+
middlewares: false,
|
|
40
|
+
circuit_breaker: nil
|
|
40
41
|
)
|
|
41
42
|
@username = username
|
|
42
43
|
@password = password
|
|
@@ -66,6 +67,11 @@ class RedisClient
|
|
|
66
67
|
@reconnect_attempts = reconnect_attempts
|
|
67
68
|
@connection_prelude = build_connection_prelude
|
|
68
69
|
|
|
70
|
+
circuit_breaker = CircuitBreaker.new(**circuit_breaker) if circuit_breaker.is_a?(Hash)
|
|
71
|
+
if @circuit_breaker = circuit_breaker
|
|
72
|
+
middlewares = [CircuitBreaker::Middleware] + (middlewares || [])
|
|
73
|
+
end
|
|
74
|
+
|
|
69
75
|
middlewares_stack = Middlewares
|
|
70
76
|
if middlewares && !middlewares.empty?
|
|
71
77
|
middlewares_stack = Class.new(Middlewares)
|
|
@@ -155,6 +161,10 @@ class RedisClient
|
|
|
155
161
|
)
|
|
156
162
|
if url
|
|
157
163
|
uri = URI(url)
|
|
164
|
+
unless uri.scheme == "redis" || uri.scheme == "rediss"
|
|
165
|
+
raise ArgumentError, "Invalid URL: #{url.inspect}"
|
|
166
|
+
end
|
|
167
|
+
|
|
158
168
|
kwargs[:ssl] = uri.scheme == "rediss" unless kwargs.key?(:ssl)
|
|
159
169
|
|
|
160
170
|
kwargs[:username] ||= uri.user if uri.password && !uri.user.empty?
|
|
@@ -17,7 +17,7 @@ class RedisClient
|
|
|
17
17
|
write(command)
|
|
18
18
|
result = read(timeout)
|
|
19
19
|
@pending_reads -= 1
|
|
20
|
-
if result.is_a?(
|
|
20
|
+
if result.is_a?(Error)
|
|
21
21
|
result._set_command(command)
|
|
22
22
|
raise result
|
|
23
23
|
else
|
|
@@ -37,7 +37,7 @@ class RedisClient
|
|
|
37
37
|
timeout = timeouts && timeouts[index]
|
|
38
38
|
result = read(timeout)
|
|
39
39
|
@pending_reads -= 1
|
|
40
|
-
if result.is_a?(
|
|
40
|
+
if result.is_a?(Error)
|
|
41
41
|
result._set_command(commands[index])
|
|
42
42
|
exception ||= result
|
|
43
43
|
end
|
|
@@ -142,7 +142,7 @@ class RedisClient
|
|
|
142
142
|
when :wait_writable
|
|
143
143
|
@io.to_io.wait_writable(@write_timeout) or raise WriteTimeoutError
|
|
144
144
|
when nil
|
|
145
|
-
raise
|
|
145
|
+
raise EOFError
|
|
146
146
|
else
|
|
147
147
|
raise "Unexpected `read_nonblock` return: #{bytes.inspect}"
|
|
148
148
|
end
|
data/lib/redis_client/version.rb
CHANGED
data/lib/redis_client.rb
CHANGED
|
@@ -62,7 +62,7 @@ class RedisClient
|
|
|
62
62
|
write_timeout: config.write_timeout
|
|
63
63
|
)
|
|
64
64
|
@config = config
|
|
65
|
-
@id = id
|
|
65
|
+
@id = id&.to_s
|
|
66
66
|
@connect_timeout = connect_timeout
|
|
67
67
|
@read_timeout = read_timeout
|
|
68
68
|
@write_timeout = write_timeout
|
|
@@ -90,9 +90,17 @@ class RedisClient
|
|
|
90
90
|
WriteTimeoutError = Class.new(TimeoutError)
|
|
91
91
|
CheckoutTimeoutError = Class.new(TimeoutError)
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
module HasCommand
|
|
94
94
|
attr_reader :command
|
|
95
95
|
|
|
96
|
+
def _set_command(command)
|
|
97
|
+
@command = command
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
class CommandError < Error
|
|
102
|
+
include HasCommand
|
|
103
|
+
|
|
96
104
|
class << self
|
|
97
105
|
def parse(error_message)
|
|
98
106
|
code = if error_message.start_with?("ERR Error running script")
|
|
@@ -107,22 +115,24 @@ class RedisClient
|
|
|
107
115
|
klass.new(error_message)
|
|
108
116
|
end
|
|
109
117
|
end
|
|
110
|
-
|
|
111
|
-
def _set_command(command)
|
|
112
|
-
@command = command
|
|
113
|
-
end
|
|
114
118
|
end
|
|
115
119
|
|
|
116
120
|
AuthenticationError = Class.new(CommandError)
|
|
117
121
|
PermissionError = Class.new(CommandError)
|
|
118
|
-
ReadOnlyError = Class.new(CommandError)
|
|
119
122
|
WrongTypeError = Class.new(CommandError)
|
|
120
123
|
OutOfMemoryError = Class.new(CommandError)
|
|
121
124
|
|
|
125
|
+
ReadOnlyError = Class.new(ConnectionError)
|
|
126
|
+
ReadOnlyError.include(HasCommand)
|
|
127
|
+
|
|
128
|
+
MasterDownError = Class.new(ConnectionError)
|
|
129
|
+
MasterDownError.include(HasCommand)
|
|
130
|
+
|
|
122
131
|
CommandError::ERRORS = {
|
|
123
132
|
"WRONGPASS" => AuthenticationError,
|
|
124
133
|
"NOPERM" => PermissionError,
|
|
125
134
|
"READONLY" => ReadOnlyError,
|
|
135
|
+
"MASTERDOWN" => MasterDownError,
|
|
126
136
|
"WRONGTYPE" => WrongTypeError,
|
|
127
137
|
"OOM" => OutOfMemoryError,
|
|
128
138
|
}.freeze
|
|
@@ -662,7 +672,7 @@ class RedisClient
|
|
|
662
672
|
prelude = config.connection_prelude.dup
|
|
663
673
|
|
|
664
674
|
if id
|
|
665
|
-
prelude << ["CLIENT", "SETNAME", id
|
|
675
|
+
prelude << ["CLIENT", "SETNAME", id]
|
|
666
676
|
end
|
|
667
677
|
|
|
668
678
|
# The connection prelude is deliberately not sent to Middlewares
|
|
@@ -681,14 +691,14 @@ class RedisClient
|
|
|
681
691
|
end
|
|
682
692
|
|
|
683
693
|
connection
|
|
684
|
-
rescue FailoverError
|
|
694
|
+
rescue FailoverError, CannotConnectError
|
|
685
695
|
raise
|
|
686
696
|
rescue ConnectionError => error
|
|
687
697
|
raise CannotConnectError, error.message, error.backtrace
|
|
688
698
|
rescue CommandError => error
|
|
689
|
-
if error.message.
|
|
699
|
+
if error.message.match?(/ERR unknown command ['`]HELLO['`]/)
|
|
690
700
|
raise UnsupportedServer,
|
|
691
|
-
"
|
|
701
|
+
"redis-client requires Redis 6+ with HELLO command available (#{config.server_url})"
|
|
692
702
|
else
|
|
693
703
|
raise
|
|
694
704
|
end
|
|
@@ -696,5 +706,6 @@ class RedisClient
|
|
|
696
706
|
end
|
|
697
707
|
|
|
698
708
|
require "redis_client/pooled"
|
|
709
|
+
require "redis_client/circuit_breaker"
|
|
699
710
|
|
|
700
711
|
RedisClient.default_driver
|
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.
|
|
4
|
+
version: 0.12.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jean Boussier
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2023-01-09 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: connection_pool
|
|
@@ -39,6 +39,7 @@ files:
|
|
|
39
39
|
- Rakefile
|
|
40
40
|
- lib/redis-client.rb
|
|
41
41
|
- lib/redis_client.rb
|
|
42
|
+
- lib/redis_client/circuit_breaker.rb
|
|
42
43
|
- lib/redis_client/command_builder.rb
|
|
43
44
|
- lib/redis_client/config.rb
|
|
44
45
|
- lib/redis_client/connection_mixin.rb
|
|
@@ -74,7 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
74
75
|
- !ruby/object:Gem::Version
|
|
75
76
|
version: '0'
|
|
76
77
|
requirements: []
|
|
77
|
-
rubygems_version: 3.
|
|
78
|
+
rubygems_version: 3.4.1
|
|
78
79
|
signing_key:
|
|
79
80
|
specification_version: 4
|
|
80
81
|
summary: Simple low-level client for Redis 6+
|