redis-cluster-client 0.9.1 → 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: 75b8f7550c743c813f79d15ba60d5e78fe5e81c751854b5600cc1984b56256fa
4
- data.tar.gz: c560f4fb9a3e5e044a64c8b8c0da8bf51413d384dd19d069994f14f44e4d4055
3
+ metadata.gz: d885c147de46fd113d493e60bd340cac591ecc94b7eafcfddbd6044abe10f79a
4
+ data.tar.gz: f13c1012ef977e8f9c3e38d928c5c3eb95ba2242761fa992033323c115b318f9
5
5
  SHA512:
6
- metadata.gz: 5165edd2f2ae4c27680be76227cc73ed1b5e5c17914dc08eb6f9015199d29bb14f0c4961f5a291c3e764b78dfee3892e6666fff6aca95bc466eab144d4ce43d4
7
- data.tar.gz: 91f09f17897dcd974f11001cd2ea0445a384cb83134d8b51835ad197835ee4710935253e024fd6c938f12fa10bb8daea56465ac7f522a6e2484ec25f942fcb0d
6
+ metadata.gz: 68c939431088425c7735c68b4af0949bc5918837d9f19878c62d71aa1fec6e31efee5de2868325447a9e4672c108299a86cc6d047447ef10e7987daa04ecb1e9
7
+ data.tar.gz: f8ae784f6977151fd825c34878e7f6162175cd828d0b81a92da07e5af96a00a56467ffbed3822f24c198f2373900dc20ea69f8446e3468fc7ba54945baa44f65
@@ -97,12 +97,6 @@ class RedisClient
97
97
  @commands.key?(::RedisClient::Cluster::NormalizedCmdName.instance.get_by_name(name))
98
98
  end
99
99
 
100
- def determine_key_step(command)
101
- name = ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_command(command)
102
- # Some commands like EVALSHA have zero as the step in COMMANDS somehow.
103
- @commands[name].key_step == 0 ? 1 : @commands[name].key_step
104
- end
105
-
106
100
  private
107
101
 
108
102
  def determine_first_key_position(command) # rubocop:disable Metrics/CyclomaticComplexity
@@ -153,6 +147,12 @@ class RedisClient
153
147
  idx = command&.flatten&.map(&:to_s)&.map(&:downcase)&.index(option_name&.downcase)
154
148
  idx.nil? ? 0 : idx + 1
155
149
  end
150
+
151
+ def determine_key_step(command)
152
+ name = ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_command(command)
153
+ # Some commands like EVALSHA have zero as the step in COMMANDS somehow.
154
+ @commands[name].key_step == 0 ? 1 : @commands[name].key_step
155
+ end
156
156
  end
157
157
  end
158
158
  end
@@ -13,9 +13,6 @@ class RedisClient
13
13
  class Node
14
14
  include Enumerable
15
15
 
16
- # It affects to strike a balance between load and stability in initialization or changed states.
17
- MAX_STARTUP_SAMPLE = Integer(ENV.fetch('REDIS_CLIENT_MAX_STARTUP_SAMPLE', 3))
18
-
19
16
  # less memory consumption, but slow
20
17
  USE_CHAR_ARRAY_SLOT = Integer(ENV.fetch('REDIS_CLIENT_USE_CHAR_ARRAY_SLOT', 1)) == 1
21
18
 
@@ -197,7 +194,7 @@ class RedisClient
197
194
 
198
195
  def reload!
199
196
  with_reload_lock do
200
- with_startup_clients(MAX_STARTUP_SAMPLE) do |startup_clients|
197
+ with_startup_clients(@config.max_startup_sample) do |startup_clients|
201
198
  @node_info = refetch_node_info_list(startup_clients)
202
199
  @node_configs = @node_info.to_h do |node_info|
203
200
  [node_info.node_key, @config.client_config_for_node(node_info.node_key)]
@@ -18,11 +18,6 @@ class RedisClient
18
18
  class Router
19
19
  ZERO_CURSOR_FOR_SCAN = '0'
20
20
  TSF = ->(f, x) { f.nil? ? x : f.call(x) }.curry
21
- MULTIPLE_KEYS_COMMAND_TO_PIPELINE = {
22
- 'mset' => 'set',
23
- 'mget' => 'get',
24
- 'del' => 'del'
25
- }.freeze
26
21
 
27
22
  def initialize(config, concurrent_worker, pool: nil, **kwargs)
28
23
  @config = config.dup
@@ -334,27 +329,31 @@ class RedisClient
334
329
  end
335
330
  end
336
331
 
332
+ MULTIPLE_KEYS_COMMAND_TO_SINGLE = {
333
+ 'mget' => ['get', 1].freeze,
334
+ 'mset' => ['set', 2].freeze,
335
+ 'del' => ['del', 1].freeze
336
+ }.freeze
337
+
337
338
  def send_multiple_keys_command(cmd, method, command, args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
338
- key_step = @command.determine_key_step(cmd)
339
- if command.size <= key_step + 1 || ::RedisClient::Cluster::KeySlotConverter.hash_tag_included?(command[1]) # rubocop:disable Style/IfUnlessModifier
340
- return try_send(assign_node(command), method, command, args, &block)
341
- end
339
+ # This implementation is prioritized performance rather than readability or so.
340
+ single_key_cmd, keys_step = MULTIPLE_KEYS_COMMAND_TO_SINGLE.fetch(cmd)
341
+
342
+ return try_send(assign_node(command), method, command, args, &block) if command.size <= keys_step + 1 || ::RedisClient::Cluster::KeySlotConverter.hash_tag_included?(command[1])
342
343
 
343
344
  seed = @config.use_replica? && @config.replica_affinity == :random ? nil : Random.new_seed
344
345
  pipeline = ::RedisClient::Cluster::Pipeline.new(self, @command_builder, @concurrent_worker, exception: true, seed: seed)
345
346
 
346
- # This implementation is prioritized to lessen memory consumption rather than readability.
347
- single_key_cmd = MULTIPLE_KEYS_COMMAND_TO_PIPELINE[cmd]
348
- single_command = Array.new(key_step + 1)
347
+ single_command = Array.new(keys_step + 1)
349
348
  single_command[0] = single_key_cmd
350
- if key_step == 1
349
+ if keys_step == 1
351
350
  command[1..].each do |key|
352
351
  single_command[1] = key
353
352
  pipeline.call_v(single_command)
354
353
  end
355
354
  else
356
- command[1..].each_slice(key_step) do |v|
357
- key_step.times { |i| single_command[i + 1] = v[i] }
355
+ command[1..].each_slice(keys_step) do |v|
356
+ keys_step.times { |i| single_command[i + 1] = v[i] }
358
357
  pipeline.call_v(single_command)
359
358
  end
360
359
  end
@@ -16,42 +16,47 @@ class RedisClient
16
16
  def initialize(config, pool: nil, concurrency: nil, **kwargs)
17
17
  @config = config
18
18
  @concurrent_worker = ::RedisClient::Cluster::ConcurrentWorker.create(**(concurrency || {}))
19
- @router = ::RedisClient::Cluster::Router.new(config, @concurrent_worker, pool: pool, **kwargs)
20
19
  @command_builder = config.command_builder
20
+
21
+ @pool = pool
22
+ @kwargs = kwargs
23
+ @router = nil
24
+ @mutex = Mutex.new
21
25
  end
22
26
 
23
27
  def inspect
24
- "#<#{self.class.name} #{@router.node_keys.join(', ')}>"
28
+ node_keys = @router.nil? ? @config.startup_nodes.keys : router.node_keys
29
+ "#<#{self.class.name} #{node_keys.join(', ')}>"
25
30
  end
26
31
 
27
32
  def call(*args, **kwargs, &block)
28
33
  command = @command_builder.generate(args, kwargs)
29
- @router.send_command(:call_v, command, &block)
34
+ router.send_command(:call_v, command, &block)
30
35
  end
31
36
 
32
37
  def call_v(command, &block)
33
38
  command = @command_builder.generate(command)
34
- @router.send_command(:call_v, command, &block)
39
+ router.send_command(:call_v, command, &block)
35
40
  end
36
41
 
37
42
  def call_once(*args, **kwargs, &block)
38
43
  command = @command_builder.generate(args, kwargs)
39
- @router.send_command(:call_once_v, command, &block)
44
+ router.send_command(:call_once_v, command, &block)
40
45
  end
41
46
 
42
47
  def call_once_v(command, &block)
43
48
  command = @command_builder.generate(command)
44
- @router.send_command(:call_once_v, command, &block)
49
+ router.send_command(:call_once_v, command, &block)
45
50
  end
46
51
 
47
52
  def blocking_call(timeout, *args, **kwargs, &block)
48
53
  command = @command_builder.generate(args, kwargs)
49
- @router.send_command(:blocking_call_v, command, timeout, &block)
54
+ router.send_command(:blocking_call_v, command, timeout, &block)
50
55
  end
51
56
 
52
57
  def blocking_call_v(timeout, command, &block)
53
58
  command = @command_builder.generate(command)
54
- @router.send_command(:blocking_call_v, command, timeout, &block)
59
+ router.send_command(:blocking_call_v, command, timeout, &block)
55
60
  end
56
61
 
57
62
  def scan(*args, **kwargs, &block)
@@ -60,31 +65,31 @@ class RedisClient
60
65
  seed = Random.new_seed
61
66
  cursor = ZERO_CURSOR_FOR_SCAN
62
67
  loop do
63
- cursor, keys = @router.scan('SCAN', cursor, *args, seed: seed, **kwargs)
68
+ cursor, keys = router.scan('SCAN', cursor, *args, seed: seed, **kwargs)
64
69
  keys.each(&block)
65
70
  break if cursor == ZERO_CURSOR_FOR_SCAN
66
71
  end
67
72
  end
68
73
 
69
74
  def sscan(key, *args, **kwargs, &block)
70
- node = @router.assign_node(['SSCAN', key])
71
- @router.try_delegate(node, :sscan, key, *args, **kwargs, &block)
75
+ node = router.assign_node(['SSCAN', key])
76
+ router.try_delegate(node, :sscan, key, *args, **kwargs, &block)
72
77
  end
73
78
 
74
79
  def hscan(key, *args, **kwargs, &block)
75
- node = @router.assign_node(['HSCAN', key])
76
- @router.try_delegate(node, :hscan, key, *args, **kwargs, &block)
80
+ node = router.assign_node(['HSCAN', key])
81
+ router.try_delegate(node, :hscan, key, *args, **kwargs, &block)
77
82
  end
78
83
 
79
84
  def zscan(key, *args, **kwargs, &block)
80
- node = @router.assign_node(['ZSCAN', key])
81
- @router.try_delegate(node, :zscan, key, *args, **kwargs, &block)
85
+ node = router.assign_node(['ZSCAN', key])
86
+ router.try_delegate(node, :zscan, key, *args, **kwargs, &block)
82
87
  end
83
88
 
84
89
  def pipelined(exception: true)
85
90
  seed = @config.use_replica? && @config.replica_affinity == :random ? nil : Random.new_seed
86
91
  pipeline = ::RedisClient::Cluster::Pipeline.new(
87
- @router,
92
+ router,
88
93
  @command_builder,
89
94
  @concurrent_worker,
90
95
  exception: exception,
@@ -99,14 +104,14 @@ class RedisClient
99
104
 
100
105
  def multi(watch: nil)
101
106
  if watch.nil? || watch.empty?
102
- transaction = ::RedisClient::Cluster::Transaction.new(@router, @command_builder)
107
+ transaction = ::RedisClient::Cluster::Transaction.new(router, @command_builder)
103
108
  yield transaction
104
109
  return transaction.execute
105
110
  end
106
111
 
107
- ::RedisClient::Cluster::OptimisticLocking.new(@router).watch(watch) do |c, slot, asking|
112
+ ::RedisClient::Cluster::OptimisticLocking.new(router).watch(watch) do |c, slot, asking|
108
113
  transaction = ::RedisClient::Cluster::Transaction.new(
109
- @router, @command_builder, node: c, slot: slot, asking: asking
114
+ router, @command_builder, node: c, slot: slot, asking: asking
110
115
  )
111
116
  yield transaction
112
117
  transaction.execute
@@ -114,7 +119,7 @@ class RedisClient
114
119
  end
115
120
 
116
121
  def pubsub
117
- ::RedisClient::Cluster::PubSub.new(@router, @command_builder)
122
+ ::RedisClient::Cluster::PubSub.new(router, @command_builder)
118
123
  end
119
124
 
120
125
  def with(...)
@@ -122,25 +127,33 @@ class RedisClient
122
127
  end
123
128
 
124
129
  def close
130
+ @router&.close
125
131
  @concurrent_worker.close
126
- @router.close
127
132
  nil
128
133
  end
129
134
 
130
135
  private
131
136
 
137
+ def router
138
+ return @router unless @router.nil?
139
+
140
+ @mutex.synchronize do
141
+ @router ||= ::RedisClient::Cluster::Router.new(@config, @concurrent_worker, pool: @pool, **@kwargs)
142
+ end
143
+ end
144
+
132
145
  def method_missing(name, *args, **kwargs, &block)
133
- if @router.command_exists?(name)
146
+ if router.command_exists?(name)
134
147
  args.unshift(name)
135
148
  command = @command_builder.generate(args, kwargs)
136
- return @router.send_command(:call_v, command, &block)
149
+ return router.send_command(:call_v, command, &block)
137
150
  end
138
151
 
139
152
  super
140
153
  end
141
154
 
142
155
  def respond_to_missing?(name, include_private = false)
143
- return true if @router.command_exists?(name)
156
+ return true if router.command_exists?(name)
144
157
 
145
158
  super
146
159
  end
@@ -20,13 +20,15 @@ class RedisClient
20
20
  MAX_WORKERS = Integer(ENV.fetch('REDIS_CLIENT_MAX_THREADS', 5))
21
21
  # It's used with slow queries of fetching meta data like CLUSTER NODES, COMMAND and so on.
22
22
  SLOW_COMMAND_TIMEOUT = Float(ENV.fetch('REDIS_CLIENT_SLOW_COMMAND_TIMEOUT', -1))
23
+ # It affects to strike a balance between load and stability in initialization or changed states.
24
+ MAX_STARTUP_SAMPLE = Integer(ENV.fetch('REDIS_CLIENT_MAX_STARTUP_SAMPLE', 3))
23
25
 
24
26
  InvalidClientConfigError = Class.new(::RedisClient::Error)
25
27
 
26
28
  attr_reader :command_builder, :client_config, :replica_affinity, :slow_command_timeout,
27
- :connect_with_original_config, :startup_nodes
29
+ :connect_with_original_config, :startup_nodes, :max_startup_sample
28
30
 
29
- def initialize(
31
+ def initialize( # rubocop:disable Metrics/ParameterLists
30
32
  nodes: DEFAULT_NODES,
31
33
  replica: false,
32
34
  replica_affinity: :random,
@@ -36,6 +38,7 @@ class RedisClient
36
38
  client_implementation: ::RedisClient::Cluster, # for redis gem
37
39
  slow_command_timeout: SLOW_COMMAND_TIMEOUT,
38
40
  command_builder: ::RedisClient::CommandBuilder,
41
+ max_startup_sample: MAX_STARTUP_SAMPLE,
39
42
  **client_config
40
43
  )
41
44
 
@@ -51,6 +54,7 @@ class RedisClient
51
54
  @connect_with_original_config = connect_with_original_config
52
55
  @client_implementation = client_implementation
53
56
  @slow_command_timeout = slow_command_timeout
57
+ @max_startup_sample = max_startup_sample
54
58
  end
55
59
 
56
60
  def inspect
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-cluster-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Taishi Kasuga
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-05-04 00:00:00.000000000 Z
11
+ date: 2024-08-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis-client
@@ -77,7 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
77
77
  - !ruby/object:Gem::Version
78
78
  version: '0'
79
79
  requirements: []
80
- rubygems_version: 3.5.9
80
+ rubygems_version: 3.5.11
81
81
  signing_key:
82
82
  specification_version: 4
83
83
  summary: A Redis cluster client for Ruby