redis-client 0.8.1 → 0.10.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 -1
- data/Gemfile.lock +1 -1
- data/README.md +13 -14
- data/Rakefile +10 -5
- data/lib/redis_client/config.rb +16 -3
- 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 +20 -16
- 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: 27142ef61a44133a72e2a087afc39986ebcfe5fae30a7ee5f6eb3f702ef35ad2
|
4
|
+
data.tar.gz: f078880a52dec2f62d3b68188cfea8108ec433742f2f7e697def7a7bece332ac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 76c7b23cb0ba419fdb9505e5cd4eb58c1c6d38d2e2fcd82778429b346dfa02a94ce4a9077c28252d8c71d1667cc45270bfc0790190e42b0d37341493bbb105f5
|
7
|
+
data.tar.gz: 071ec50fabaa72512101d0f408154e432e5f9950927607d8d6bcf8c7f3d5d4c9f49f40de3a24aa6957019751a598b70ae9db525002c03d7beb05312fc082e053
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,18 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 0.10.0
|
4
|
+
|
5
|
+
- Added instance scoped middlewares. See: #53
|
6
|
+
- Allow subclasses of accepted types as command arguments. Fix: #51
|
7
|
+
- Improve hiredis driver error messages.
|
8
|
+
|
9
|
+
# 0.9.0
|
10
|
+
|
11
|
+
- Automatically reconnect if the process was forked.
|
12
|
+
|
3
13
|
# 0.8.1
|
4
14
|
|
5
|
-
- Make the client resilient to `Timeout.timeout` or `Thread#kill` use (it still
|
15
|
+
- Make the client resilient to `Timeout.timeout` or `Thread#kill` use (it still is very much discouraged to use either).
|
6
16
|
Use of async interrupts could cause responses to be interleaved.
|
7
17
|
- hiredis: handle commands returning a top-level `false` (no command does this today, but some extensions might).
|
8
18
|
- Workaround a bug in Ruby 2.6 causing a crash if the `debug` gem is enabled when `redis-client` is being required. Fix: #48
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -343,12 +343,13 @@ end
|
|
343
343
|
|
344
344
|
## Production
|
345
345
|
|
346
|
-
### Instrumentation
|
346
|
+
### Instrumentation and Middlewares
|
347
347
|
|
348
|
-
`redis-client` offers a public
|
348
|
+
`redis-client` offers a public middleware API to aid in monitoring and library extension. Middleware can be registered
|
349
|
+
either globally or on a given configuration instance.
|
349
350
|
|
350
351
|
```ruby
|
351
|
-
module
|
352
|
+
module MyGlobalRedisInstrumentation
|
352
353
|
def connect(redis_config)
|
353
354
|
MyMonitoringService.instrument("redis.connect") { super }
|
354
355
|
end
|
@@ -361,10 +362,17 @@ module MyRedisInstrumentation
|
|
361
362
|
MyMonitoringService.instrument("redis.pipeline") { super }
|
362
363
|
end
|
363
364
|
end
|
364
|
-
RedisClient.register(
|
365
|
+
RedisClient.register(MyGlobalRedisInstrumentation)
|
365
366
|
```
|
366
367
|
|
367
|
-
Note that
|
368
|
+
Note that `RedisClient.register` is global and apply to all `RedisClient` instances.
|
369
|
+
|
370
|
+
To add middlewares to only a single client, you can provide them when creating the config:
|
371
|
+
|
372
|
+
```ruby
|
373
|
+
redis_config = RedisClient.config(middlewares: [AnotherRedisInstrumentation])
|
374
|
+
redis_config.new_client
|
375
|
+
```
|
368
376
|
|
369
377
|
### Timeouts
|
370
378
|
|
@@ -437,15 +445,6 @@ Contrary to the `redis` gem, `redis-client` doesn't protect against concurrent a
|
|
437
445
|
To use `redis-client` in concurrent environments, you MUST use a connection pool, or
|
438
446
|
have one client per Thread or Fiber.
|
439
447
|
|
440
|
-
### Fork Safety
|
441
|
-
|
442
|
-
`redis-client` doesn't try to detect forked processes. You MUST disconnect all clients before forking your process.
|
443
|
-
|
444
|
-
```ruby
|
445
|
-
redis.close
|
446
|
-
Process.fork ...
|
447
|
-
```
|
448
|
-
|
449
448
|
## Development
|
450
449
|
|
451
450
|
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
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
@@ -12,8 +12,9 @@ class RedisClient
|
|
12
12
|
DEFAULT_DB = 0
|
13
13
|
|
14
14
|
module Common
|
15
|
-
attr_reader :db, :password, :id, :ssl, :ssl_params, :command_builder,
|
16
|
-
:connect_timeout, :read_timeout, :write_timeout, :driver, :connection_prelude, :protocol
|
15
|
+
attr_reader :db, :password, :id, :ssl, :ssl_params, :command_builder, :inherit_socket,
|
16
|
+
:connect_timeout, :read_timeout, :write_timeout, :driver, :connection_prelude, :protocol,
|
17
|
+
:middlewares_stack
|
17
18
|
|
18
19
|
alias_method :ssl?, :ssl
|
19
20
|
|
@@ -32,7 +33,9 @@ class RedisClient
|
|
32
33
|
protocol: 3,
|
33
34
|
client_implementation: RedisClient,
|
34
35
|
command_builder: CommandBuilder,
|
35
|
-
|
36
|
+
inherit_socket: false,
|
37
|
+
reconnect_attempts: false,
|
38
|
+
middlewares: false
|
36
39
|
)
|
37
40
|
@username = username
|
38
41
|
@password = password
|
@@ -54,10 +57,20 @@ class RedisClient
|
|
54
57
|
end
|
55
58
|
|
56
59
|
@command_builder = command_builder
|
60
|
+
@inherit_socket = inherit_socket
|
57
61
|
|
58
62
|
reconnect_attempts = Array.new(reconnect_attempts, 0).freeze if reconnect_attempts.is_a?(Integer)
|
59
63
|
@reconnect_attempts = reconnect_attempts
|
60
64
|
@connection_prelude = build_connection_prelude
|
65
|
+
|
66
|
+
middlewares_stack = Middlewares
|
67
|
+
if middlewares && !middlewares.empty?
|
68
|
+
middlewares_stack = Class.new(Middlewares)
|
69
|
+
middlewares.each do |mod|
|
70
|
+
middlewares_stack.include(mod)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
@middlewares_stack = middlewares_stack
|
61
74
|
end
|
62
75
|
|
63
76
|
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
@@ -67,6 +67,7 @@ class RedisClient
|
|
67
67
|
@read_timeout = read_timeout
|
68
68
|
@write_timeout = write_timeout
|
69
69
|
@command_builder = config.command_builder
|
70
|
+
@pid = Process.pid
|
70
71
|
end
|
71
72
|
|
72
73
|
def timeout=(timeout)
|
@@ -144,7 +145,7 @@ class RedisClient
|
|
144
145
|
end
|
145
146
|
|
146
147
|
def register(middleware)
|
147
|
-
Middlewares.
|
148
|
+
Middlewares.include(middleware)
|
148
149
|
end
|
149
150
|
end
|
150
151
|
|
@@ -152,6 +153,7 @@ class RedisClient
|
|
152
153
|
|
153
154
|
def initialize(config, **)
|
154
155
|
super
|
156
|
+
@middlewares = config.middlewares_stack.new(self)
|
155
157
|
@raw_connection = nil
|
156
158
|
@disable_reconnection = false
|
157
159
|
end
|
@@ -194,7 +196,7 @@ class RedisClient
|
|
194
196
|
def call(*command, **kwargs)
|
195
197
|
command = @command_builder.generate(command, kwargs)
|
196
198
|
result = ensure_connected do |connection|
|
197
|
-
|
199
|
+
@middlewares.call(command, config) do
|
198
200
|
connection.call(command, nil)
|
199
201
|
end
|
200
202
|
end
|
@@ -209,7 +211,7 @@ class RedisClient
|
|
209
211
|
def call_v(command)
|
210
212
|
command = @command_builder.generate(command)
|
211
213
|
result = ensure_connected do |connection|
|
212
|
-
|
214
|
+
@middlewares.call(command, config) do
|
213
215
|
connection.call(command, nil)
|
214
216
|
end
|
215
217
|
end
|
@@ -224,7 +226,7 @@ class RedisClient
|
|
224
226
|
def call_once(*command, **kwargs)
|
225
227
|
command = @command_builder.generate(command, kwargs)
|
226
228
|
result = ensure_connected(retryable: false) do |connection|
|
227
|
-
|
229
|
+
@middlewares.call(command, config) do
|
228
230
|
connection.call(command, nil)
|
229
231
|
end
|
230
232
|
end
|
@@ -239,7 +241,7 @@ class RedisClient
|
|
239
241
|
def call_once_v(command)
|
240
242
|
command = @command_builder.generate(command)
|
241
243
|
result = ensure_connected(retryable: false) do |connection|
|
242
|
-
|
244
|
+
@middlewares.call(command, config) do
|
243
245
|
connection.call(command, nil)
|
244
246
|
end
|
245
247
|
end
|
@@ -255,7 +257,7 @@ class RedisClient
|
|
255
257
|
command = @command_builder.generate(command, kwargs)
|
256
258
|
error = nil
|
257
259
|
result = ensure_connected do |connection|
|
258
|
-
|
260
|
+
@middlewares.call(command, config) do
|
259
261
|
connection.call(command, timeout)
|
260
262
|
end
|
261
263
|
rescue ReadTimeoutError => error
|
@@ -275,7 +277,7 @@ class RedisClient
|
|
275
277
|
command = @command_builder.generate(command)
|
276
278
|
error = nil
|
277
279
|
result = ensure_connected do |connection|
|
278
|
-
|
280
|
+
@middlewares.call(command, config) do
|
279
281
|
connection.call(command, timeout)
|
280
282
|
end
|
281
283
|
rescue ReadTimeoutError => error
|
@@ -346,7 +348,7 @@ class RedisClient
|
|
346
348
|
else
|
347
349
|
results = ensure_connected(retryable: pipeline._retryable?) do |connection|
|
348
350
|
commands = pipeline._commands
|
349
|
-
|
351
|
+
@middlewares.call_pipelined(commands, config) do
|
350
352
|
connection.call_pipelined(commands, pipeline._timeouts)
|
351
353
|
end
|
352
354
|
end
|
@@ -366,7 +368,7 @@ class RedisClient
|
|
366
368
|
begin
|
367
369
|
if transaction = build_transaction(&block)
|
368
370
|
commands = transaction._commands
|
369
|
-
results =
|
371
|
+
results = @middlewares.call_pipelined(commands, config) do
|
370
372
|
connection.call_pipelined(commands, nil)
|
371
373
|
end.last
|
372
374
|
else
|
@@ -385,7 +387,7 @@ class RedisClient
|
|
385
387
|
else
|
386
388
|
ensure_connected(retryable: transaction._retryable?) do |connection|
|
387
389
|
commands = transaction._commands
|
388
|
-
|
390
|
+
@middlewares.call_pipelined(commands, config) do
|
389
391
|
connection.call_pipelined(commands, nil)
|
390
392
|
end.last
|
391
393
|
end
|
@@ -416,7 +418,7 @@ class RedisClient
|
|
416
418
|
end
|
417
419
|
|
418
420
|
def close
|
419
|
-
raw_connection&.close
|
421
|
+
@raw_connection&.close
|
420
422
|
@raw_connection = nil
|
421
423
|
self
|
422
424
|
end
|
@@ -597,6 +599,8 @@ class RedisClient
|
|
597
599
|
end
|
598
600
|
|
599
601
|
def ensure_connected(retryable: true)
|
602
|
+
close if !config.inherit_socket && @pid != Process.pid
|
603
|
+
|
600
604
|
if @disable_reconnection
|
601
605
|
if block_given?
|
602
606
|
yield @raw_connection
|
@@ -614,7 +618,6 @@ class RedisClient
|
|
614
618
|
connection
|
615
619
|
end
|
616
620
|
rescue ConnectionError, ProtocolError => error
|
617
|
-
connection&.close
|
618
621
|
close
|
619
622
|
|
620
623
|
if !@disable_reconnection && config.retry_connecting?(tries, error)
|
@@ -631,7 +634,6 @@ class RedisClient
|
|
631
634
|
@disable_reconnection = true
|
632
635
|
yield connection
|
633
636
|
rescue ConnectionError, ProtocolError
|
634
|
-
connection&.close
|
635
637
|
close
|
636
638
|
raise
|
637
639
|
ensure
|
@@ -646,7 +648,9 @@ class RedisClient
|
|
646
648
|
end
|
647
649
|
|
648
650
|
def connect
|
649
|
-
|
651
|
+
@pid = Process.pid
|
652
|
+
|
653
|
+
connection = @middlewares.connect(config) do
|
650
654
|
config.driver.new(
|
651
655
|
config,
|
652
656
|
connect_timeout: connect_timeout,
|
@@ -664,13 +668,13 @@ class RedisClient
|
|
664
668
|
# The connection prelude is deliberately not sent to Middlewares
|
665
669
|
if config.sentinel?
|
666
670
|
prelude << ["ROLE"]
|
667
|
-
role, =
|
671
|
+
role, = @middlewares.call_pipelined(prelude, config) do
|
668
672
|
connection.call_pipelined(prelude, nil).last
|
669
673
|
end
|
670
674
|
config.check_role!(role)
|
671
675
|
else
|
672
676
|
unless prelude.empty?
|
673
|
-
|
677
|
+
@middlewares.call_pipelined(prelude, config) do
|
674
678
|
connection.call_pipelined(prelude, nil)
|
675
679
|
end
|
676
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.10.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-10-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: connection_pool
|