redis-client 0.14.1 → 0.16.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 +11 -0
- data/Gemfile.lock +4 -4
- data/README.md +13 -7
- data/lib/redis_client/config.rb +11 -27
- data/lib/redis_client/ruby_connection.rb +6 -0
- data/lib/redis_client/sentinel_config.rb +23 -7
- data/lib/redis_client/url_config.rb +53 -0
- data/lib/redis_client/version.rb +1 -1
- data/lib/redis_client.rb +13 -0
- 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: 35ff65a4af6127cb60acff6a3d3b2d2e5e65ddeffc0e8e808b7dec90792697a6
|
4
|
+
data.tar.gz: 854f82d7548c5462b53ae29a637dc208cdc70b3399b28d0c61f38e02f073e37e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c9e8e564662e86f4e1a24ca9528a5d909a096218eaf62bd7b58838402db390e7bec65d79a43aa89a2b98d22f9645ca7e412ae2db4010dc4131cbf2133d2666f
|
7
|
+
data.tar.gz: c6400693a10e28e377bf52792ee54f41afb2b2c97ea49578a90eba767b6e3b6a7f37eaeaccf41a38a8222827f461f44aa0b1f75881084dc30a521e407508e277
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 0.16.0
|
4
|
+
|
5
|
+
- Add `RedisClient#disable_reconnection`.
|
6
|
+
- Reverted the special discard of connection. A regular `close(2)` should be enough.
|
7
|
+
|
8
|
+
# 0.15.0
|
9
|
+
|
10
|
+
- Discard sockets rather than explictly close them when a fork is detected. #126.
|
11
|
+
- Allow to configure sentinel client via url. #117.
|
12
|
+
- Fix sentinel to preverse the auth/password when refreshing the sentinel list. #107.
|
13
|
+
|
3
14
|
# 0.14.1
|
4
15
|
|
5
16
|
- Include the timeout value in TimeoutError messages.
|
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.16.0)
|
5
5
|
connection_pool
|
6
6
|
|
7
7
|
GEM
|
@@ -10,7 +10,7 @@ GEM
|
|
10
10
|
ast (2.4.2)
|
11
11
|
benchmark-ips (2.12.0)
|
12
12
|
byebug (11.1.3)
|
13
|
-
connection_pool (2.4.
|
13
|
+
connection_pool (2.4.1)
|
14
14
|
hiredis (0.6.3)
|
15
15
|
hiredis (0.6.3-java)
|
16
16
|
minitest (5.15.0)
|
@@ -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.5)
|
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.25)
|
42
42
|
toxiproxy (2.0.2)
|
43
43
|
unicode-display_width (2.2.0)
|
44
44
|
|
data/README.md
CHANGED
@@ -42,7 +42,7 @@ redis.with do |r|
|
|
42
42
|
end
|
43
43
|
```
|
44
44
|
|
45
|
-
If you are working in a single
|
45
|
+
If you are working in a single-threaded environment, or wish to use your own connection pooling mechanism,
|
46
46
|
you can obtain a raw client with `#new_client`
|
47
47
|
|
48
48
|
```ruby
|
@@ -67,11 +67,11 @@ redis.call("GET", "mykey")
|
|
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.
|
70
|
-
- `ssl`:
|
70
|
+
- `ssl`: Whether to connect using SSL or not.
|
71
71
|
- `ssl_params`: A configuration Hash passed to [`OpenSSL::SSL::SSLContext#set_params`](https://www.rubydoc.info/stdlib/openssl/OpenSSL%2FSSL%2FSSLContext:set_params), notable options include:
|
72
72
|
- `cert`: The path to the client certificate (e.g. `client.crt`).
|
73
73
|
- `key`: The path to the client key (e.g. `client.key`).
|
74
|
-
- `ca_file`: The certificate authority to use, useful for self
|
74
|
+
- `ca_file`: The certificate authority to use, useful for self-signed certificates (e.g. `ca.crt`),
|
75
75
|
- `db`: The database to select after connecting, defaults to `0`.
|
76
76
|
- `id` ID for the client connection, assigns name to current connection by sending `CLIENT SETNAME`.
|
77
77
|
- `username` Username to authenticate against server, defaults to `"default"`.
|
@@ -83,7 +83,7 @@ redis.call("GET", "mykey")
|
|
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
84
|
- `circuit_breaker`: A Hash with circuit breaker configuration. Defaults to `nil`. See the [circuit breaker section](#circuit-breaker) for details.
|
85
85
|
- `protocol:` The version of the RESP protocol to use. Default to `3`.
|
86
|
-
- `custom`: A user
|
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 metadata.
|
87
87
|
|
88
88
|
### Sentinel support
|
89
89
|
|
@@ -343,6 +343,10 @@ loop do
|
|
343
343
|
end
|
344
344
|
```
|
345
345
|
|
346
|
+
*Note*: pubsub connections are stateful, as such they won't ever reconnect automatically.
|
347
|
+
The caller is responsible for reconnecting if the connection is lost and to resubscribe to
|
348
|
+
all channels.
|
349
|
+
|
346
350
|
## Production
|
347
351
|
|
348
352
|
### Instrumentation and Middlewares
|
@@ -376,7 +380,7 @@ redis_config = RedisClient.config(middlewares: [AnotherRedisInstrumentation])
|
|
376
380
|
redis_config.new_client
|
377
381
|
```
|
378
382
|
|
379
|
-
If middlewares need a client
|
383
|
+
If middlewares need a client-specific configuration, `Config#custom` can be used
|
380
384
|
|
381
385
|
```ruby
|
382
386
|
module MyGlobalRedisInstrumentation
|
@@ -439,6 +443,8 @@ redis.call("GET", "counter") # Will be retried up to 3 times.
|
|
439
443
|
redis.call_once("INCR", "counter") # Won't be retried.
|
440
444
|
```
|
441
445
|
|
446
|
+
**Note**: automatic reconnection doesn't apply to pubsub clients as their connection is stateful.
|
447
|
+
|
442
448
|
### Exponential backoff
|
443
449
|
|
444
450
|
Alternatively, `reconnect_attempts` accepts a list of sleep durations for implementing exponential backoff:
|
@@ -448,9 +454,9 @@ redis_config = RedisClient.config(reconnect_attempts: [0, 0.05, 0.1])
|
|
448
454
|
```
|
449
455
|
|
450
456
|
This configuration is generally used when the Redis server is expected to failover or recover relatively quickly and
|
451
|
-
that it's not really
|
457
|
+
that it's not really possible to continue without issuing the command.
|
452
458
|
|
453
|
-
When the Redis server is used as an ephemeral cache, circuit breakers are generally
|
459
|
+
When the Redis server is used as an ephemeral cache, circuit breakers are generally preferred.
|
454
460
|
|
455
461
|
### Circuit Breaker
|
456
462
|
|
data/lib/redis_client/config.rb
CHANGED
@@ -162,37 +162,21 @@ class RedisClient
|
|
162
162
|
**kwargs
|
163
163
|
)
|
164
164
|
if url
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
kwargs[:password] ||= if uri.user && !uri.password
|
175
|
-
URI.decode_www_form_component(uri.user)
|
176
|
-
elsif uri.user && uri.password
|
177
|
-
URI.decode_www_form_component(uri.password)
|
178
|
-
end
|
179
|
-
|
180
|
-
db_path = uri.path&.delete_prefix("/")
|
181
|
-
kwargs[:db] ||= Integer(db_path) if db_path && !db_path.empty?
|
165
|
+
url_config = URLConfig.new(url)
|
166
|
+
kwargs = {
|
167
|
+
ssl: url_config.ssl?,
|
168
|
+
username: url_config.username,
|
169
|
+
password: url_config.password,
|
170
|
+
db: url_config.db,
|
171
|
+
}.compact.merge(kwargs)
|
172
|
+
host ||= url_config.host
|
173
|
+
port ||= url_config.port
|
182
174
|
end
|
183
175
|
|
184
176
|
super(**kwargs)
|
185
177
|
|
186
|
-
@host = host
|
187
|
-
|
188
|
-
uri_host = uri&.host
|
189
|
-
uri_host = nil if uri_host&.empty?
|
190
|
-
if uri_host
|
191
|
-
@host = uri_host&.sub(/\A\[(.*)\]\z/, '\1')
|
192
|
-
end
|
193
|
-
end
|
194
|
-
@host ||= DEFAULT_HOST
|
195
|
-
@port = Integer(port || uri&.port || DEFAULT_PORT)
|
178
|
+
@host = host || DEFAULT_HOST
|
179
|
+
@port = Integer(port || DEFAULT_PORT)
|
196
180
|
@path = path
|
197
181
|
end
|
198
182
|
end
|
@@ -101,6 +101,12 @@ class RedisClient
|
|
101
101
|
raise ConnectionError, error.message
|
102
102
|
end
|
103
103
|
|
104
|
+
def measure_round_trip_delay
|
105
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
|
106
|
+
call(["PING"], @read_timeout)
|
107
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) - start
|
108
|
+
end
|
109
|
+
|
104
110
|
private
|
105
111
|
|
106
112
|
def connect
|
@@ -7,11 +7,28 @@ class RedisClient
|
|
7
7
|
SENTINEL_DELAY = 0.25
|
8
8
|
DEFAULT_RECONNECT_ATTEMPTS = 2
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
attr_reader :name
|
11
|
+
|
12
|
+
def initialize(sentinels:, role: :master, name: nil, url: nil, **client_config)
|
13
|
+
unless %i(master replica slave).include?(role.to_sym)
|
12
14
|
raise ArgumentError, "Expected role to be either :master or :replica, got: #{role.inspect}"
|
13
15
|
end
|
14
16
|
|
17
|
+
if url
|
18
|
+
url_config = URLConfig.new(url)
|
19
|
+
client_config = {
|
20
|
+
username: url_config.username,
|
21
|
+
password: url_config.password,
|
22
|
+
db: url_config.db,
|
23
|
+
}.compact.merge(client_config)
|
24
|
+
name ||= url_config.host
|
25
|
+
end
|
26
|
+
|
27
|
+
@name = name
|
28
|
+
unless @name
|
29
|
+
raise ArgumentError, "RedisClient::SentinelConfig requires either a name or an url with a host"
|
30
|
+
end
|
31
|
+
|
15
32
|
@to_list_of_hash = @to_hash = nil
|
16
33
|
@extra_config = {}
|
17
34
|
if client_config[:protocol] == 2
|
@@ -25,16 +42,15 @@ class RedisClient
|
|
25
42
|
end
|
26
43
|
end
|
27
44
|
|
28
|
-
@name = name
|
29
|
-
@sentinel_configs = sentinels_to_configs(sentinels)
|
30
45
|
@sentinels = {}.compare_by_identity
|
31
|
-
@role = role
|
46
|
+
@role = role.to_sym
|
32
47
|
@mutex = Mutex.new
|
33
48
|
@config = nil
|
34
49
|
|
35
50
|
client_config[:reconnect_attempts] ||= DEFAULT_RECONNECT_ATTEMPTS
|
36
51
|
@client_config = client_config || {}
|
37
52
|
super(**client_config)
|
53
|
+
@sentinel_configs = sentinels_to_configs(sentinels)
|
38
54
|
end
|
39
55
|
|
40
56
|
def sentinels
|
@@ -90,9 +106,9 @@ class RedisClient
|
|
90
106
|
sentinels.map do |sentinel|
|
91
107
|
case sentinel
|
92
108
|
when String
|
93
|
-
Config.new(**@extra_config, url: sentinel)
|
109
|
+
Config.new(**@client_config, **@extra_config, url: sentinel, db: nil)
|
94
110
|
else
|
95
|
-
Config.new(**@extra_config, **sentinel)
|
111
|
+
Config.new(**@client_config, **@extra_config, **sentinel, db: nil)
|
96
112
|
end
|
97
113
|
end
|
98
114
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "uri"
|
4
|
+
|
5
|
+
class RedisClient
|
6
|
+
class URLConfig
|
7
|
+
DEFAULT_SCHEMA = "redis"
|
8
|
+
SSL_SCHEMA = "rediss"
|
9
|
+
|
10
|
+
attr_reader :url, :uri
|
11
|
+
|
12
|
+
def initialize(url)
|
13
|
+
@url = url
|
14
|
+
@uri = URI(url)
|
15
|
+
unless uri.scheme == DEFAULT_SCHEMA || uri.scheme == SSL_SCHEMA
|
16
|
+
raise ArgumentError, "Invalid URL: #{url.inspect}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def ssl?
|
21
|
+
@uri.scheme == SSL_SCHEMA
|
22
|
+
end
|
23
|
+
|
24
|
+
def db
|
25
|
+
db_path = uri.path&.delete_prefix("/")
|
26
|
+
Integer(db_path) if db_path && !db_path.empty?
|
27
|
+
end
|
28
|
+
|
29
|
+
def username
|
30
|
+
uri.user if uri.password && !uri.user.empty?
|
31
|
+
end
|
32
|
+
|
33
|
+
def password
|
34
|
+
if uri.user && !uri.password
|
35
|
+
URI.decode_www_form_component(uri.user)
|
36
|
+
elsif uri.user && uri.password
|
37
|
+
URI.decode_www_form_component(uri.password)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def host
|
42
|
+
return if uri.host.nil? || uri.host.empty?
|
43
|
+
|
44
|
+
uri.host.sub(/\A\[(.*)\]\z/, '\1')
|
45
|
+
end
|
46
|
+
|
47
|
+
def port
|
48
|
+
return unless uri.port
|
49
|
+
|
50
|
+
Integer(uri.port)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/redis_client/version.rb
CHANGED
data/lib/redis_client.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "redis_client/version"
|
4
4
|
require "redis_client/command_builder"
|
5
|
+
require "redis_client/url_config"
|
5
6
|
require "redis_client/config"
|
6
7
|
require "redis_client/pid_cache"
|
7
8
|
require "redis_client/sentinel_config"
|
@@ -204,6 +205,14 @@ class RedisClient
|
|
204
205
|
sub
|
205
206
|
end
|
206
207
|
|
208
|
+
def measure_round_trip_delay
|
209
|
+
ensure_connected do |connection|
|
210
|
+
@middlewares.call(["PING"], config) do
|
211
|
+
connection.measure_round_trip_delay
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
207
216
|
def call(*command, **kwargs)
|
208
217
|
command = @command_builder.generate(command, kwargs)
|
209
218
|
result = ensure_connected do |connection|
|
@@ -349,6 +358,10 @@ class RedisClient
|
|
349
358
|
self
|
350
359
|
end
|
351
360
|
|
361
|
+
def disable_reconnection(&block)
|
362
|
+
ensure_connected(retryable: false, &block)
|
363
|
+
end
|
364
|
+
|
352
365
|
def pipelined
|
353
366
|
pipeline = Pipeline.new(@command_builder)
|
354
367
|
yield pipeline
|
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.16.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: 2023-
|
11
|
+
date: 2023-08-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: connection_pool
|
@@ -51,6 +51,7 @@ files:
|
|
51
51
|
- lib/redis_client/ruby_connection/buffered_io.rb
|
52
52
|
- lib/redis_client/ruby_connection/resp3.rb
|
53
53
|
- lib/redis_client/sentinel_config.rb
|
54
|
+
- lib/redis_client/url_config.rb
|
54
55
|
- lib/redis_client/version.rb
|
55
56
|
- redis-client.gemspec
|
56
57
|
homepage: https://github.com/redis-rb/redis-client
|
@@ -76,7 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
76
77
|
- !ruby/object:Gem::Version
|
77
78
|
version: '0'
|
78
79
|
requirements: []
|
79
|
-
rubygems_version: 3.4.
|
80
|
+
rubygems_version: 3.4.10
|
80
81
|
signing_key:
|
81
82
|
specification_version: 4
|
82
83
|
summary: Simple low-level client for Redis 6+
|