redis-cluster-client 0.12.0 → 0.12.1

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: 2465b865cd79c78cca3a1e7e3950989cce94f6675b0497763e03e261d3fa9c6f
4
- data.tar.gz: df607279a58171aca75835b767f3bf5965256db0fcb7b540bfcb3a8f302eb638
3
+ metadata.gz: 5d90c095f5058e808331eaa9219fb52b9d4a27de639a9c54d194c68ecb253611
4
+ data.tar.gz: 8cd8092db87c7cfa42e00c6cabd30747bdea74130d8eb1e19b971b95637665fa
5
5
  SHA512:
6
- metadata.gz: bfb9031660b642bb14768dc8edc66fb15073be9ca23ddc061d279a351c497a83ff8a3aad21903e2d0a37aa6824da35761188ba4dc1fe8ae0182b54584edaa5e5
7
- data.tar.gz: 9fab63d0595bfb7dac85a07c255a41573ab8d44598605919fd4182e99cf3da585bf931abe63da9e2f470815c6ef0d34ab24a5b7f2de346ba29870fb894c556ee
6
+ metadata.gz: 4821b78b0d5766566fa3943f65271908d3e52e3f82269bd2ea0a789e571df7818b5e0275c88d8833eb7b449c7c0eb3ed672034c1caf03fac35528114f31adb73
7
+ data.tar.gz: a4bb1d4a766cd742645c61342c2822e2bd337c792fb67b9793bfbb06ec0a889185eaa8b3ed34f96591f9e57997ecfe83cc901ce65451577b7e8f13d7d23f64b3
@@ -17,7 +17,6 @@ class RedisClient
17
17
  Detail = Struct.new(
18
18
  'RedisCommand',
19
19
  :first_key_position,
20
- :last_key_position,
21
20
  :key_step,
22
21
  :write?,
23
22
  :readonly?,
@@ -54,7 +53,6 @@ class RedisClient
54
53
 
55
54
  acc[row[0].downcase] = ::RedisClient::Cluster::Command::Detail.new(
56
55
  first_key_position: row[3],
57
- last_key_position: row[4],
58
56
  key_step: row[5],
59
57
  write?: row[2].include?('write'),
60
58
  readonly?: row[2].include?('readonly')
@@ -71,18 +69,7 @@ class RedisClient
71
69
  i = determine_first_key_position(command)
72
70
  return EMPTY_STRING if i == 0
73
71
 
74
- (command[i].is_a?(Array) ? command[i].flatten.first : command[i]).to_s
75
- end
76
-
77
- def extract_all_keys(command)
78
- keys_start = determine_first_key_position(command)
79
- keys_end = determine_last_key_position(command, keys_start)
80
- keys_step = determine_key_step(command)
81
- return EMPTY_ARRAY if [keys_start, keys_end, keys_step].any?(&:zero?)
82
-
83
- keys_end = [keys_end, command.size - 1].min
84
- # use .. inclusive range because keys_end is a valid index.
85
- (keys_start..keys_end).step(keys_step).map { |i| command[i] }
72
+ command[i]
86
73
  end
87
74
 
88
75
  def should_send_to_primary?(command)
@@ -116,45 +103,10 @@ class RedisClient
116
103
  end
117
104
  end
118
105
 
119
- # IMPORTANT: this determines the last key position INCLUSIVE of the last key -
120
- # i.e. command[determine_last_key_position(command)] is a key.
121
- # This is in line with what Redis returns from COMMANDS.
122
- def determine_last_key_position(command, keys_start) # rubocop:disable Metrics/AbcSize
123
- case name = ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_command(command)
124
- when 'eval', 'evalsha', 'zinterstore', 'zunionstore'
125
- # EVALSHA sha1 numkeys [key [key ...]] [arg [arg ...]]
126
- # ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE <SUM | MIN | MAX>]
127
- command[2].to_i + 2
128
- when 'object', 'memory'
129
- # OBJECT [ENCODING | FREQ | IDLETIME | REFCOUNT] key
130
- # MEMORY USAGE key [SAMPLES count]
131
- keys_start
132
- when 'migrate'
133
- # MIGRATE host port <key | ""> destination-db timeout [COPY] [REPLACE] [AUTH password | AUTH2 username password] [KEYS key [key ...]]
134
- command[3].empty? ? (command.length - 1) : 3
135
- when 'xread', 'xreadgroup'
136
- # XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id [id ...]
137
- keys_start + ((command.length - keys_start) / 2) - 1
138
- else
139
- # If there is a fixed, non-variable number of keys, don't iterate past that.
140
- if @commands[name].last_key_position >= 0
141
- @commands[name].last_key_position
142
- else
143
- command.length + @commands[name].last_key_position
144
- end
145
- end
146
- end
147
-
148
- def determine_optional_key_position(command, option_name) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
149
- idx = command&.flatten&.map(&:to_s)&.map(&:downcase)&.index(option_name&.downcase)
106
+ def determine_optional_key_position(command, option_name)
107
+ idx = command.map { |e| e.to_s.downcase }.index(option_name&.downcase)
150
108
  idx.nil? ? 0 : idx + 1
151
109
  end
152
-
153
- def determine_key_step(command)
154
- name = ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_command(command)
155
- # Some commands like EVALSHA have zero as the step in COMMANDS somehow.
156
- @commands[name].key_step == 0 ? 1 : @commands[name].key_step
157
- end
158
110
  end
159
111
  end
160
112
  end
@@ -34,11 +34,14 @@ class RedisClient
34
34
  end
35
35
 
36
36
  class ErrorCollection < Error
37
+ EMPTY_HASH = {}.freeze
38
+
39
+ private_constant :EMPTY_HASH
37
40
  attr_reader :errors
38
41
 
39
42
  def self.with_errors(errors)
40
43
  if !errors.is_a?(Hash) || errors.empty?
41
- new(errors.to_s).with_errors({})
44
+ new(errors.to_s).with_errors(EMPTY_HASH)
42
45
  else
43
46
  messages = errors.map { |node_key, error| "#{node_key}: (#{error.class}) #{error.message}" }
44
47
  new(messages.join(', ')).with_errors(errors)
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RedisClient
4
+ class Cluster
5
+ module NoopCommandBuilder
6
+ module_function
7
+
8
+ def generate(args, _kwargs = nil)
9
+ args
10
+ end
11
+ end
12
+ end
13
+ end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'redis_client'
4
4
  require 'redis_client/cluster/errors'
5
+ require 'redis_client/cluster/noop_command_builder'
5
6
  require 'redis_client/connection_mixin'
6
7
  require 'redis_client/middlewares'
7
8
  require 'redis_client/pooled'
@@ -229,7 +230,7 @@ class RedisClient
229
230
 
230
231
  def append_pipeline(node_key)
231
232
  @pipelines ||= {}
232
- @pipelines[node_key] ||= ::RedisClient::Cluster::Pipeline::Extended.new(@command_builder)
233
+ @pipelines[node_key] ||= ::RedisClient::Cluster::Pipeline::Extended.new(::RedisClient::Cluster::NoopCommandBuilder)
233
234
  @pipelines[node_key].add_outer_index(@size)
234
235
  @size += 1
235
236
  @pipelines[node_key]
@@ -104,12 +104,6 @@ class RedisClient
104
104
  end
105
105
  end
106
106
 
107
- def try_delegate(node, method, *args, retry_count: 3, **kwargs, &block)
108
- handle_redirection(node, nil, retry_count: retry_count) do |on_node|
109
- on_node.public_send(method, *args, **kwargs, &block)
110
- end
111
- end
112
-
113
107
  def handle_redirection(node, command, retry_count:) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
114
108
  yield node
115
109
  rescue ::RedisClient::CircuitBreaker::OpenCircuitError
@@ -153,9 +147,7 @@ class RedisClient
153
147
  raise
154
148
  end
155
149
 
156
- def scan(*command, seed: nil, **kwargs) # rubocop:disable Metrics/AbcSize
157
- command = @command_builder.generate(command, kwargs)
158
-
150
+ def scan(command, seed: nil) # rubocop:disable Metrics/AbcSize
159
151
  command[1] = ZERO_CURSOR_FOR_SCAN if command.size == 1
160
152
  input_cursor = Integer(command[1])
161
153
 
@@ -180,6 +172,16 @@ class RedisClient
180
172
  raise
181
173
  end
182
174
 
175
+ def scan_single_key(command, arity:, &block)
176
+ node = assign_node(command)
177
+ loop do
178
+ cursor, values = handle_redirection(node, nil, retry_count: 3) { |n| n.call_v(command) }
179
+ command[2] = cursor
180
+ arity < 2 ? values.each(&block) : values.each_slice(arity, &block)
181
+ break if cursor == ZERO_CURSOR_FOR_SCAN
182
+ end
183
+ end
184
+
183
185
  def assign_node(command)
184
186
  handle_node_reload_error do
185
187
  node_key = find_node_key(command)
@@ -365,17 +367,20 @@ class RedisClient
365
367
  end
366
368
  end
367
369
 
368
- MULTIPLE_KEYS_COMMAND_TO_SINGLE = {
369
- 'mget' => ['get', 1].freeze,
370
- 'mset' => ['set', 2].freeze,
371
- 'del' => ['del', 1].freeze
372
- }.freeze
373
-
374
- private_constant :MULTIPLE_KEYS_COMMAND_TO_SINGLE
375
-
376
370
  def send_multiple_keys_command(cmd, method, command, args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
377
371
  # This implementation is prioritized performance rather than readability or so.
378
- single_key_cmd, keys_step = MULTIPLE_KEYS_COMMAND_TO_SINGLE.fetch(cmd)
372
+ case cmd
373
+ when 'mget'
374
+ single_key_cmd = 'get'
375
+ keys_step = 1
376
+ when 'mset'
377
+ single_key_cmd = 'set'
378
+ keys_step = 2
379
+ when 'del'
380
+ single_key_cmd = 'del'
381
+ keys_step = 1
382
+ else raise NotImplementedError, cmd
383
+ end
379
384
 
380
385
  return try_send(assign_node(command), method, command, args, &block) if command.size <= keys_step + 1 || ::RedisClient::Cluster::KeySlotConverter.hash_tag_included?(command[1])
381
386
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'redis_client'
4
4
  require 'redis_client/cluster/errors'
5
+ require 'redis_client/cluster/noop_command_builder'
5
6
  require 'redis_client/cluster/pipeline'
6
7
 
7
8
  class RedisClient
@@ -18,7 +19,7 @@ class RedisClient
18
19
  @router = router
19
20
  @command_builder = command_builder
20
21
  @retryable = true
21
- @pipeline = ::RedisClient::Pipeline.new(@command_builder)
22
+ @pipeline = ::RedisClient::Pipeline.new(::RedisClient::Cluster::NoopCommandBuilder)
22
23
  @pending_commands = []
23
24
  @node = node
24
25
  prepare_tx unless @node.nil?
@@ -62,30 +62,37 @@ class RedisClient
62
62
  end
63
63
 
64
64
  def scan(*args, **kwargs, &block)
65
- raise ArgumentError, 'block required' unless block
65
+ return to_enum(__callee__, *args, **kwargs) unless block_given?
66
66
 
67
+ command = @command_builder.generate(['SCAN', ZERO_CURSOR_FOR_SCAN] + args, kwargs)
67
68
  seed = Random.new_seed
68
- cursor = ZERO_CURSOR_FOR_SCAN
69
69
  loop do
70
- cursor, keys = router.scan('SCAN', cursor, *args, seed: seed, **kwargs)
70
+ cursor, keys = router.scan(command, seed: seed)
71
+ command[1] = cursor
71
72
  keys.each(&block)
72
73
  break if cursor == ZERO_CURSOR_FOR_SCAN
73
74
  end
74
75
  end
75
76
 
76
77
  def sscan(key, *args, **kwargs, &block)
77
- node = router.assign_node(['SSCAN', key])
78
- router.try_delegate(node, :sscan, key, *args, **kwargs, &block)
78
+ return to_enum(__callee__, key, *args, **kwargs) unless block_given?
79
+
80
+ command = @command_builder.generate(['SSCAN', key, ZERO_CURSOR_FOR_SCAN] + args, kwargs)
81
+ router.scan_single_key(command, arity: 1, &block)
79
82
  end
80
83
 
81
84
  def hscan(key, *args, **kwargs, &block)
82
- node = router.assign_node(['HSCAN', key])
83
- router.try_delegate(node, :hscan, key, *args, **kwargs, &block)
85
+ return to_enum(__callee__, key, *args, **kwargs) unless block_given?
86
+
87
+ command = @command_builder.generate(['HSCAN', key, ZERO_CURSOR_FOR_SCAN] + args, kwargs)
88
+ router.scan_single_key(command, arity: 2, &block)
84
89
  end
85
90
 
86
91
  def zscan(key, *args, **kwargs, &block)
87
- node = router.assign_node(['ZSCAN', key])
88
- router.try_delegate(node, :zscan, key, *args, **kwargs, &block)
92
+ return to_enum(__callee__, key, *args, **kwargs) unless block_given?
93
+
94
+ command = @command_builder.generate(['ZSCAN', key, ZERO_CURSOR_FOR_SCAN] + args, kwargs)
95
+ router.scan_single_key(command, arity: 2, &block)
89
96
  end
90
97
 
91
98
  def pipelined(exception: true)
@@ -5,6 +5,7 @@ require 'redis_client'
5
5
  require 'redis_client/cluster'
6
6
  require 'redis_client/cluster/errors'
7
7
  require 'redis_client/cluster/node_key'
8
+ require 'redis_client/cluster/noop_command_builder'
8
9
  require 'redis_client/command_builder'
9
10
 
10
11
  class RedisClient
@@ -64,7 +65,7 @@ class RedisClient
64
65
  end
65
66
 
66
67
  def inspect
67
- "#<#{self.class.name} #{startup_nodes.values}>"
68
+ "#<#{self.class.name} #{startup_nodes.values.map { |v| v.reject { |k| k == :command_builder } }}>"
68
69
  end
69
70
 
70
71
  def read_timeout
@@ -187,6 +188,7 @@ class RedisClient
187
188
  def augment_client_config(config)
188
189
  config = @client_config.merge(config)
189
190
  config = config.merge(host: @fixed_hostname) unless @fixed_hostname.empty?
191
+ config[:command_builder] = ::RedisClient::Cluster::NoopCommandBuilder # prevent twice call
190
192
  config
191
193
  end
192
194
  end
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.12.0
4
+ version: 0.12.1
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-11-14 00:00:00.000000000 Z
11
+ date: 2024-11-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis-client
@@ -48,6 +48,7 @@ files:
48
48
  - lib/redis_client/cluster/node/random_replica.rb
49
49
  - lib/redis_client/cluster/node/random_replica_or_primary.rb
50
50
  - lib/redis_client/cluster/node_key.rb
51
+ - lib/redis_client/cluster/noop_command_builder.rb
51
52
  - lib/redis_client/cluster/normalized_cmd_name.rb
52
53
  - lib/redis_client/cluster/optimistic_locking.rb
53
54
  - lib/redis_client/cluster/pipeline.rb