redis-client 0.9.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/Gemfile.lock +2 -2
- data/README.md +34 -5
- data/Rakefile +10 -5
- data/lib/redis_client/config.rb +16 -2
- data/lib/redis_client/middlewares.rb +9 -2
- data/lib/redis_client/ruby_connection/resp3.rb +8 -4
- data/lib/redis_client/version.rb +1 -1
- data/lib/redis_client.rb +14 -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: 7f28d878a923f684e1ddade2a735339dc7ae55d3091b3d21b812fcf1b232b8fe
|
4
|
+
data.tar.gz: 5cc874fa373695185c4b5f040e8368be66550596838faadab8b3e57e45f8c0b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba5313f360e675d1e751f6c5138290facab7e3ea043d5328b14556050ceb44155947ad670077e22b1fb04b2aa5738a6d6b77454b5d31e0a4641c1e705f3ba545
|
7
|
+
data.tar.gz: 45463aae9b8d188cc5d1a2c0604962001bafcd266fb526fecb7ddee5081fbc28a9a5a27b8a09525b515d69266cf7740424a50408e62cf2361debe1ffb52b3d9e
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 0.11.0
|
4
|
+
|
5
|
+
- hiredis: do not eagerly close the connection on read timeout, let the caller decide if a timeout is final.
|
6
|
+
- Add `Config#custom` to store configuration metadata. It can be used for per server middleware configuration.
|
7
|
+
|
8
|
+
# 0.10.0
|
9
|
+
|
10
|
+
- Added instance scoped middlewares. See: #53
|
11
|
+
- Allow subclasses of accepted types as command arguments. Fix: #51
|
12
|
+
- Improve hiredis driver error messages.
|
13
|
+
|
3
14
|
# 0.9.0
|
4
15
|
|
5
16
|
- Automatically reconnect if the process was forked.
|
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.11.0)
|
5
5
|
connection_pool
|
6
6
|
|
7
7
|
GEM
|
@@ -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.22)
|
42
42
|
toxiproxy (2.0.2)
|
43
43
|
unicode-display_width (2.2.0)
|
44
44
|
|
data/README.md
CHANGED
@@ -82,6 +82,7 @@ redis.call("GET", "mykey")
|
|
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
84
|
- `protocol:` The version of the RESP protocol to use. Default to `3`.
|
85
|
+
- `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.
|
85
86
|
|
86
87
|
### Sentinel support
|
87
88
|
|
@@ -343,12 +344,13 @@ end
|
|
343
344
|
|
344
345
|
## Production
|
345
346
|
|
346
|
-
### Instrumentation
|
347
|
+
### Instrumentation and Middlewares
|
347
348
|
|
348
|
-
`redis-client` offers a public
|
349
|
+
`redis-client` offers a public middleware API to aid in monitoring and library extension. Middleware can be registered
|
350
|
+
either globally or on a given configuration instance.
|
349
351
|
|
350
352
|
```ruby
|
351
|
-
module
|
353
|
+
module MyGlobalRedisInstrumentation
|
352
354
|
def connect(redis_config)
|
353
355
|
MyMonitoringService.instrument("redis.connect") { super }
|
354
356
|
end
|
@@ -361,11 +363,38 @@ module MyRedisInstrumentation
|
|
361
363
|
MyMonitoringService.instrument("redis.pipeline") { super }
|
362
364
|
end
|
363
365
|
end
|
364
|
-
RedisClient.register(
|
366
|
+
RedisClient.register(MyGlobalRedisInstrumentation)
|
365
367
|
```
|
366
368
|
|
367
|
-
Note that
|
369
|
+
Note that `RedisClient.register` is global and apply to all `RedisClient` instances.
|
368
370
|
|
371
|
+
To add middlewares to only a single client, you can provide them when creating the config:
|
372
|
+
|
373
|
+
```ruby
|
374
|
+
redis_config = RedisClient.config(middlewares: [AnotherRedisInstrumentation])
|
375
|
+
redis_config.new_client
|
376
|
+
```
|
377
|
+
|
378
|
+
If middlewares need a client specific configuration, `Config#custom` can be used
|
379
|
+
|
380
|
+
```ruby
|
381
|
+
module MyGlobalRedisInstrumentation
|
382
|
+
def connect(redis_config)
|
383
|
+
MyMonitoringService.instrument("redis.connect", tags: redis_config.custom[:tags]) { super }
|
384
|
+
end
|
385
|
+
|
386
|
+
def call(command, redis_config)
|
387
|
+
MyMonitoringService.instrument("redis.query", tags: redis_config.custom[:tags]) { super }
|
388
|
+
end
|
389
|
+
|
390
|
+
def call_pipelined(commands, redis_config)
|
391
|
+
MyMonitoringService.instrument("redis.pipeline", tags: redis_config.custom[:tags]) { super }
|
392
|
+
end
|
393
|
+
end
|
394
|
+
RedisClient.register(MyGlobalRedisInstrumentation)
|
395
|
+
|
396
|
+
redis_config = RedisClient.config(custom: { tags: { "environment": Rails.env }})
|
397
|
+
```
|
369
398
|
### Timeouts
|
370
399
|
|
371
400
|
The client allows you to configure connect, read, and write timeouts.
|
data/Rakefile
CHANGED
@@ -67,12 +67,15 @@ namespace :hiredis do
|
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
+
benchmark_suites = %w(single pipelined)
|
71
|
+
benchmark_modes = %i[ruby yjit hiredis]
|
70
72
|
namespace :benchmark do
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
output_path = "benchmark/#{
|
73
|
+
benchmark_suites.each do |suite|
|
74
|
+
benchmark_modes.each do |mode|
|
75
|
+
name = "#{suite}_#{mode}"
|
76
|
+
task name do
|
77
|
+
output_path = "benchmark/#{name}.md"
|
78
|
+
sh "rm", "-f", output_path
|
76
79
|
File.open(output_path, "w+") do |output|
|
77
80
|
output.puts("ruby: `#{RUBY_DESCRIPTION}`\n\n")
|
78
81
|
output.puts("redis-server: `#{`redis-server -v`.strip}`\n\n")
|
@@ -103,6 +106,8 @@ namespace :benchmark do
|
|
103
106
|
end
|
104
107
|
end
|
105
108
|
end
|
109
|
+
|
110
|
+
task all: benchmark_suites.flat_map { |s| benchmark_modes.flat_map { |m| "#{s}_#{m}" } }
|
106
111
|
end
|
107
112
|
|
108
113
|
if hiredis_supported
|
data/lib/redis_client/config.rb
CHANGED
@@ -13,7 +13,8 @@ class RedisClient
|
|
13
13
|
|
14
14
|
module Common
|
15
15
|
attr_reader :db, :password, :id, :ssl, :ssl_params, :command_builder, :inherit_socket,
|
16
|
-
:connect_timeout, :read_timeout, :write_timeout, :driver, :connection_prelude, :protocol
|
16
|
+
:connect_timeout, :read_timeout, :write_timeout, :driver, :connection_prelude, :protocol,
|
17
|
+
:middlewares_stack, :custom
|
17
18
|
|
18
19
|
alias_method :ssl?, :ssl
|
19
20
|
|
@@ -27,13 +28,15 @@ class RedisClient
|
|
27
28
|
write_timeout: timeout,
|
28
29
|
connect_timeout: timeout,
|
29
30
|
ssl: nil,
|
31
|
+
custom: {},
|
30
32
|
ssl_params: nil,
|
31
33
|
driver: nil,
|
32
34
|
protocol: 3,
|
33
35
|
client_implementation: RedisClient,
|
34
36
|
command_builder: CommandBuilder,
|
35
37
|
inherit_socket: false,
|
36
|
-
reconnect_attempts: false
|
38
|
+
reconnect_attempts: false,
|
39
|
+
middlewares: false
|
37
40
|
)
|
38
41
|
@username = username
|
39
42
|
@password = password
|
@@ -48,6 +51,8 @@ class RedisClient
|
|
48
51
|
|
49
52
|
@driver = driver ? RedisClient.driver(driver) : RedisClient.default_driver
|
50
53
|
|
54
|
+
@custom = custom
|
55
|
+
|
51
56
|
@client_implementation = client_implementation
|
52
57
|
@protocol = protocol
|
53
58
|
unless protocol == 2 || protocol == 3
|
@@ -60,6 +65,15 @@ class RedisClient
|
|
60
65
|
reconnect_attempts = Array.new(reconnect_attempts, 0).freeze if reconnect_attempts.is_a?(Integer)
|
61
66
|
@reconnect_attempts = reconnect_attempts
|
62
67
|
@connection_prelude = build_connection_prelude
|
68
|
+
|
69
|
+
middlewares_stack = Middlewares
|
70
|
+
if middlewares && !middlewares.empty?
|
71
|
+
middlewares_stack = Class.new(Middlewares)
|
72
|
+
middlewares.each do |mod|
|
73
|
+
middlewares_stack.include(mod)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
@middlewares_stack = middlewares_stack
|
63
77
|
end
|
64
78
|
|
65
79
|
def username
|
@@ -1,8 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class RedisClient
|
4
|
-
|
5
|
-
|
4
|
+
class BasicMiddleware
|
5
|
+
attr_reader :client
|
6
|
+
|
7
|
+
def initialize(client)
|
8
|
+
@client = client
|
9
|
+
end
|
6
10
|
|
7
11
|
def connect(_config)
|
8
12
|
yield
|
@@ -13,4 +17,7 @@ class RedisClient
|
|
13
17
|
end
|
14
18
|
alias_method :call_pipelined, :call
|
15
19
|
end
|
20
|
+
|
21
|
+
class Middlewares < BasicMiddleware
|
22
|
+
end
|
16
23
|
end
|
@@ -10,12 +10,12 @@ class RedisClient
|
|
10
10
|
|
11
11
|
EOL = "\r\n".b.freeze
|
12
12
|
EOL_SIZE = EOL.bytesize
|
13
|
-
DUMP_TYPES = {
|
13
|
+
DUMP_TYPES = { # rubocop:disable Style/MutableConstant
|
14
14
|
String => :dump_string,
|
15
15
|
Symbol => :dump_symbol,
|
16
16
|
Integer => :dump_numeric,
|
17
17
|
Float => :dump_numeric,
|
18
|
-
}
|
18
|
+
}
|
19
19
|
PARSER_TYPES = {
|
20
20
|
'#' => :parse_boolean,
|
21
21
|
'$' => :parse_blob,
|
@@ -55,8 +55,12 @@ class RedisClient
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def dump_any(object, buffer)
|
58
|
-
method = DUMP_TYPES.fetch(object.class) do
|
59
|
-
|
58
|
+
method = DUMP_TYPES.fetch(object.class) do |unexpected_class|
|
59
|
+
if superclass = DUMP_TYPES.keys.find { |t| t > unexpected_class }
|
60
|
+
DUMP_TYPES[unexpected_class] = DUMP_TYPES[superclass]
|
61
|
+
else
|
62
|
+
raise TypeError, "Unsupported command argument type: #{unexpected_class}"
|
63
|
+
end
|
60
64
|
end
|
61
65
|
send(method, object, buffer)
|
62
66
|
end
|
data/lib/redis_client/version.rb
CHANGED
data/lib/redis_client.rb
CHANGED
@@ -145,7 +145,7 @@ class RedisClient
|
|
145
145
|
end
|
146
146
|
|
147
147
|
def register(middleware)
|
148
|
-
Middlewares.
|
148
|
+
Middlewares.include(middleware)
|
149
149
|
end
|
150
150
|
end
|
151
151
|
|
@@ -153,6 +153,7 @@ class RedisClient
|
|
153
153
|
|
154
154
|
def initialize(config, **)
|
155
155
|
super
|
156
|
+
@middlewares = config.middlewares_stack.new(self)
|
156
157
|
@raw_connection = nil
|
157
158
|
@disable_reconnection = false
|
158
159
|
end
|
@@ -195,7 +196,7 @@ class RedisClient
|
|
195
196
|
def call(*command, **kwargs)
|
196
197
|
command = @command_builder.generate(command, kwargs)
|
197
198
|
result = ensure_connected do |connection|
|
198
|
-
|
199
|
+
@middlewares.call(command, config) do
|
199
200
|
connection.call(command, nil)
|
200
201
|
end
|
201
202
|
end
|
@@ -210,7 +211,7 @@ class RedisClient
|
|
210
211
|
def call_v(command)
|
211
212
|
command = @command_builder.generate(command)
|
212
213
|
result = ensure_connected do |connection|
|
213
|
-
|
214
|
+
@middlewares.call(command, config) do
|
214
215
|
connection.call(command, nil)
|
215
216
|
end
|
216
217
|
end
|
@@ -225,7 +226,7 @@ class RedisClient
|
|
225
226
|
def call_once(*command, **kwargs)
|
226
227
|
command = @command_builder.generate(command, kwargs)
|
227
228
|
result = ensure_connected(retryable: false) do |connection|
|
228
|
-
|
229
|
+
@middlewares.call(command, config) do
|
229
230
|
connection.call(command, nil)
|
230
231
|
end
|
231
232
|
end
|
@@ -240,7 +241,7 @@ class RedisClient
|
|
240
241
|
def call_once_v(command)
|
241
242
|
command = @command_builder.generate(command)
|
242
243
|
result = ensure_connected(retryable: false) do |connection|
|
243
|
-
|
244
|
+
@middlewares.call(command, config) do
|
244
245
|
connection.call(command, nil)
|
245
246
|
end
|
246
247
|
end
|
@@ -256,7 +257,7 @@ class RedisClient
|
|
256
257
|
command = @command_builder.generate(command, kwargs)
|
257
258
|
error = nil
|
258
259
|
result = ensure_connected do |connection|
|
259
|
-
|
260
|
+
@middlewares.call(command, config) do
|
260
261
|
connection.call(command, timeout)
|
261
262
|
end
|
262
263
|
rescue ReadTimeoutError => error
|
@@ -276,7 +277,7 @@ class RedisClient
|
|
276
277
|
command = @command_builder.generate(command)
|
277
278
|
error = nil
|
278
279
|
result = ensure_connected do |connection|
|
279
|
-
|
280
|
+
@middlewares.call(command, config) do
|
280
281
|
connection.call(command, timeout)
|
281
282
|
end
|
282
283
|
rescue ReadTimeoutError => error
|
@@ -347,7 +348,7 @@ class RedisClient
|
|
347
348
|
else
|
348
349
|
results = ensure_connected(retryable: pipeline._retryable?) do |connection|
|
349
350
|
commands = pipeline._commands
|
350
|
-
|
351
|
+
@middlewares.call_pipelined(commands, config) do
|
351
352
|
connection.call_pipelined(commands, pipeline._timeouts)
|
352
353
|
end
|
353
354
|
end
|
@@ -367,7 +368,7 @@ class RedisClient
|
|
367
368
|
begin
|
368
369
|
if transaction = build_transaction(&block)
|
369
370
|
commands = transaction._commands
|
370
|
-
results =
|
371
|
+
results = @middlewares.call_pipelined(commands, config) do
|
371
372
|
connection.call_pipelined(commands, nil)
|
372
373
|
end.last
|
373
374
|
else
|
@@ -386,7 +387,7 @@ class RedisClient
|
|
386
387
|
else
|
387
388
|
ensure_connected(retryable: transaction._retryable?) do |connection|
|
388
389
|
commands = transaction._commands
|
389
|
-
|
390
|
+
@middlewares.call_pipelined(commands, config) do
|
390
391
|
connection.call_pipelined(commands, nil)
|
391
392
|
end.last
|
392
393
|
end
|
@@ -649,7 +650,7 @@ class RedisClient
|
|
649
650
|
def connect
|
650
651
|
@pid = Process.pid
|
651
652
|
|
652
|
-
connection =
|
653
|
+
connection = @middlewares.connect(config) do
|
653
654
|
config.driver.new(
|
654
655
|
config,
|
655
656
|
connect_timeout: connect_timeout,
|
@@ -667,13 +668,13 @@ class RedisClient
|
|
667
668
|
# The connection prelude is deliberately not sent to Middlewares
|
668
669
|
if config.sentinel?
|
669
670
|
prelude << ["ROLE"]
|
670
|
-
role, =
|
671
|
+
role, = @middlewares.call_pipelined(prelude, config) do
|
671
672
|
connection.call_pipelined(prelude, nil).last
|
672
673
|
end
|
673
674
|
config.check_role!(role)
|
674
675
|
else
|
675
676
|
unless prelude.empty?
|
676
|
-
|
677
|
+
@middlewares.call_pipelined(prelude, config) do
|
677
678
|
connection.call_pipelined(prelude, nil)
|
678
679
|
end
|
679
680
|
end
|
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.11.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: 2022-
|
11
|
+
date: 2022-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: connection_pool
|