redis-cluster-client 0.12.0 → 0.12.1
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/lib/redis_client/cluster/command.rb +3 -51
- data/lib/redis_client/cluster/errors.rb +4 -1
- data/lib/redis_client/cluster/noop_command_builder.rb +13 -0
- data/lib/redis_client/cluster/pipeline.rb +2 -1
- data/lib/redis_client/cluster/router.rb +23 -18
- data/lib/redis_client/cluster/transaction.rb +2 -1
- data/lib/redis_client/cluster.rb +16 -9
- data/lib/redis_client/cluster_config.rb +3 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d90c095f5058e808331eaa9219fb52b9d4a27de639a9c54d194c68ecb253611
|
4
|
+
data.tar.gz: 8cd8092db87c7cfa42e00c6cabd30747bdea74130d8eb1e19b971b95637665fa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
|
120
|
-
|
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)
|
@@ -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(
|
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(
|
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
|
-
|
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(
|
22
|
+
@pipeline = ::RedisClient::Pipeline.new(::RedisClient::Cluster::NoopCommandBuilder)
|
22
23
|
@pending_commands = []
|
23
24
|
@node = node
|
24
25
|
prepare_tx unless @node.nil?
|
data/lib/redis_client/cluster.rb
CHANGED
@@ -62,30 +62,37 @@ class RedisClient
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def scan(*args, **kwargs, &block)
|
65
|
-
|
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(
|
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
|
-
|
78
|
-
|
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
|
-
|
83
|
-
|
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
|
-
|
88
|
-
|
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.
|
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-
|
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
|