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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 95dd96eda7210df59b85fafb8d2df5fa46cf0d3d2da10ad1ebf60137b8ec2652
4
- data.tar.gz: d417951e19d9a20b7f96a2d4e520d433cd52c38209d8c46499ddfc0d6d4be941
3
+ metadata.gz: 7f28d878a923f684e1ddade2a735339dc7ae55d3091b3d21b812fcf1b232b8fe
4
+ data.tar.gz: 5cc874fa373695185c4b5f040e8368be66550596838faadab8b3e57e45f8c0b0
5
5
  SHA512:
6
- metadata.gz: 0dd66a81c969a08abbb89140187e6a3a31aadcb258845082bf84bba792304ebc3d27852635fa4519805139eeecb7f405033b2b0c6acc4d39f7823873cd91dbdc
7
- data.tar.gz: 4a9f5b79ffc8d195a3fac2cd439750d43124bb78583eef840564262d25565b2e585d8d77eb3f9d2096ec938fa4db7fe7a775c5ba5f80c4e2bd91f2b121901a98
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.9.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.21)
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 instrumentation API monitoring tools.
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 MyRedisInstrumentation
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(MyRedisInstrumentation)
366
+ RedisClient.register(MyGlobalRedisInstrumentation)
365
367
  ```
366
368
 
367
- Note that this instrumentation is global.
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
- task :record do
72
- system("rm -rf tmp/*.benchmark")
73
- %w(single pipelined).each do |suite|
74
- %i[ruby yjit hiredis].each do |mode|
75
- output_path = "benchmark/#{suite}_#{mode}.md"
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
@@ -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
- module Middlewares
5
- extend self
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
- }.freeze
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
- raise TypeError, "Unsupported command argument type: #{object.class}"
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class RedisClient
4
- VERSION = "0.9.0"
4
+ VERSION = "0.11.0"
5
5
  end
data/lib/redis_client.rb CHANGED
@@ -145,7 +145,7 @@ class RedisClient
145
145
  end
146
146
 
147
147
  def register(middleware)
148
- Middlewares.extend(middleware)
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
- Middlewares.call(command, config) do
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
- Middlewares.call(command, config) do
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
- Middlewares.call(command, config) do
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
- Middlewares.call(command, config) do
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
- Middlewares.call(command, config) do
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
- Middlewares.call(command, config) do
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
- Middlewares.call_pipelined(commands, config) do
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 = Middlewares.call_pipelined(commands, config) do
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
- Middlewares.call_pipelined(commands, config) do
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 = Middlewares.connect(config) do
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, = Middlewares.call_pipelined(prelude, config) do
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
- Middlewares.call_pipelined(prelude, config) do
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.9.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-09-22 00:00:00.000000000 Z
11
+ date: 2022-11-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: connection_pool