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 +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
|