redis-cluster-client 0.3.2 → 0.3.4
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 +13 -6
- data/lib/redis_client/cluster/node.rb +2 -0
- data/lib/redis_client/cluster/normalized_cmd_name.rb +67 -0
- data/lib/redis_client/cluster/pipeline.rb +22 -17
- data/lib/redis_client/cluster/router.rb +17 -11
- data/lib/redis_client/cluster.rb +2 -1
- data/lib/redis_client/cluster_config.rb +4 -0
- 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: 458c3d9cc4d1f6595510663cede0403953b2555ace0d3cae814663b23f69f1cb
|
4
|
+
data.tar.gz: 01d2a34641e298ff7e99cc5f9edbad4ed152e5ca4f18e16181b9a4b84a2492c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3680e6b8b1594001060eb2dea29eb60f0779bba6a43350246b1d5c7e47b1bb1e04a6e1188ac87c1228adac21e86347cb3820679d6c92b693d7f7fedabba88402
|
7
|
+
data.tar.gz: 21927b8a14e9ad075e3808e736789c4339af907f5d99e16806273f1553c505e7fc86faabfa067b98f2a3dd7d45a41fb0f89e780e08825aa9df4317531871da5d
|
@@ -2,10 +2,13 @@
|
|
2
2
|
|
3
3
|
require 'redis_client'
|
4
4
|
require 'redis_client/cluster/errors'
|
5
|
+
require 'redis_client/cluster/normalized_cmd_name'
|
5
6
|
|
6
7
|
class RedisClient
|
7
8
|
class Cluster
|
8
9
|
class Command
|
10
|
+
EMPTY_STRING = ''
|
11
|
+
|
9
12
|
class << self
|
10
13
|
def load(nodes) # rubocop:disable Metrics/MethodLength
|
11
14
|
errors = []
|
@@ -29,7 +32,10 @@ class RedisClient
|
|
29
32
|
|
30
33
|
def parse_command_details(rows)
|
31
34
|
rows&.reject { |row| row[0].nil? }.to_h do |row|
|
32
|
-
[
|
35
|
+
[
|
36
|
+
::RedisClient::Cluster::NormalizedCmdName.instance.get_by_name(row[0]),
|
37
|
+
{ arity: row[1], flags: row[2], first: row[3], last: row[4], step: row[5] }
|
38
|
+
]
|
33
39
|
end
|
34
40
|
end
|
35
41
|
end
|
@@ -40,7 +46,7 @@ class RedisClient
|
|
40
46
|
|
41
47
|
def extract_first_key(command)
|
42
48
|
i = determine_first_key_position(command)
|
43
|
-
return
|
49
|
+
return EMPTY_STRING if i == 0
|
44
50
|
|
45
51
|
key = (command[i].is_a?(Array) ? command[i].flatten.first : command[i]).to_s
|
46
52
|
hash_tag = extract_hash_tag(key)
|
@@ -56,7 +62,8 @@ class RedisClient
|
|
56
62
|
end
|
57
63
|
|
58
64
|
def exists?(name)
|
59
|
-
|
65
|
+
key = ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_name(name)
|
66
|
+
@details.key?(key)
|
60
67
|
end
|
61
68
|
|
62
69
|
private
|
@@ -72,14 +79,14 @@ class RedisClient
|
|
72
79
|
end
|
73
80
|
|
74
81
|
def dig_details(command, key)
|
75
|
-
name =
|
82
|
+
name = ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_command(command)
|
76
83
|
return if name.empty? || !@details.key?(name)
|
77
84
|
|
78
85
|
@details.fetch(name).fetch(key)
|
79
86
|
end
|
80
87
|
|
81
88
|
def determine_first_key_position(command) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
82
|
-
case
|
89
|
+
case ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_command(command)
|
83
90
|
when 'eval', 'evalsha', 'zinterstore', 'zunionstore' then 3
|
84
91
|
when 'object' then 2
|
85
92
|
when 'memory'
|
@@ -104,7 +111,7 @@ class RedisClient
|
|
104
111
|
s = key.index('{')
|
105
112
|
e = key.index('}', s.to_i + 1)
|
106
113
|
|
107
|
-
return
|
114
|
+
return EMPTY_STRING if s.nil? || e.nil?
|
108
115
|
|
109
116
|
key[s + 1..e - 1]
|
110
117
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
|
5
|
+
class RedisClient
|
6
|
+
class Cluster
|
7
|
+
class NormalizedCmdName
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
EMPTY_STRING = ''
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@cache = {}
|
14
|
+
@mutex = Mutex.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_by_command(command)
|
18
|
+
get(command, index: 0)
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_by_subcommand(command)
|
22
|
+
get(command, index: 1)
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_by_name(name)
|
26
|
+
get(name, index: 0)
|
27
|
+
end
|
28
|
+
|
29
|
+
def clear
|
30
|
+
@mutex.synchronize { @cache.clear }
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def get(command, index:)
|
36
|
+
name = extract_name(command, index: index)
|
37
|
+
return EMPTY_STRING if name.nil? || name.empty?
|
38
|
+
|
39
|
+
normalize(name)
|
40
|
+
end
|
41
|
+
|
42
|
+
def extract_name(command, index:)
|
43
|
+
case command
|
44
|
+
when String, Symbol then index.zero? ? command : nil
|
45
|
+
when Array then extract_name_from_array(command, index: index)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def extract_name_from_array(command, index:)
|
50
|
+
return if command.size - 1 < index
|
51
|
+
|
52
|
+
case e = command[index]
|
53
|
+
when String, Symbol then e
|
54
|
+
when Array then e[index]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def normalize(name)
|
59
|
+
return @cache[name] if @cache.key?(name)
|
60
|
+
return name.to_s.downcase if @mutex.locked?
|
61
|
+
|
62
|
+
@mutex.synchronize { @cache[name] = name.to_s.downcase }
|
63
|
+
@cache[name]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -9,54 +9,48 @@ class RedisClient
|
|
9
9
|
ReplySizeError = Class.new(::RedisClient::Error)
|
10
10
|
MAX_THREADS = Integer(ENV.fetch('REDIS_CLIENT_MAX_THREADS', 5))
|
11
11
|
|
12
|
-
def initialize(router, command_builder)
|
12
|
+
def initialize(router, command_builder, seed: Random.new_seed)
|
13
13
|
@router = router
|
14
14
|
@command_builder = command_builder
|
15
|
-
@grouped =
|
15
|
+
@grouped = {}
|
16
16
|
@size = 0
|
17
|
-
@seed =
|
17
|
+
@seed = seed
|
18
18
|
end
|
19
19
|
|
20
20
|
def call(*args, **kwargs, &block)
|
21
21
|
command = @command_builder.generate(args, kwargs)
|
22
22
|
node_key = @router.find_node_key(command, seed: @seed)
|
23
|
-
|
24
|
-
@size += 1
|
23
|
+
add_line(node_key, [@size, :call_v, command, block])
|
25
24
|
end
|
26
25
|
|
27
26
|
def call_v(args, &block)
|
28
27
|
command = @command_builder.generate(args)
|
29
28
|
node_key = @router.find_node_key(command, seed: @seed)
|
30
|
-
|
31
|
-
@size += 1
|
29
|
+
add_line(node_key, [@size, :call_v, command, block])
|
32
30
|
end
|
33
31
|
|
34
32
|
def call_once(*args, **kwargs, &block)
|
35
33
|
command = @command_builder.generate(args, kwargs)
|
36
34
|
node_key = @router.find_node_key(command, seed: @seed)
|
37
|
-
|
38
|
-
@size += 1
|
35
|
+
add_line(node_key, [@size, :call_once_v, command, block])
|
39
36
|
end
|
40
37
|
|
41
38
|
def call_once_v(args, &block)
|
42
39
|
command = @command_builder.generate(args)
|
43
40
|
node_key = @router.find_node_key(command, seed: @seed)
|
44
|
-
|
45
|
-
@size += 1
|
41
|
+
add_line(node_key, [@size, :call_once_v, command, block])
|
46
42
|
end
|
47
43
|
|
48
44
|
def blocking_call(timeout, *args, **kwargs, &block)
|
49
45
|
command = @command_builder.generate(args, kwargs)
|
50
46
|
node_key = @router.find_node_key(command, seed: @seed)
|
51
|
-
|
52
|
-
@size += 1
|
47
|
+
add_line(node_key, [@size, :blocking_call_v, timeout, command, block])
|
53
48
|
end
|
54
49
|
|
55
50
|
def blocking_call_v(timeout, args, &block)
|
56
51
|
command = @command_builder.generate(args)
|
57
52
|
node_key = @router.find_node_key(command, seed: @seed)
|
58
|
-
|
59
|
-
@size += 1
|
53
|
+
add_line(node_key, [@size, :blocking_call_v, timeout, command, block])
|
60
54
|
end
|
61
55
|
|
62
56
|
def empty?
|
@@ -72,8 +66,11 @@ class RedisClient
|
|
72
66
|
Thread.new(@router, k, v) do |router, node_key, rows|
|
73
67
|
Thread.pass
|
74
68
|
replies = router.find_node(node_key).pipelined do |pipeline|
|
75
|
-
rows.each do |
|
76
|
-
|
69
|
+
rows.each do |row|
|
70
|
+
case row.size
|
71
|
+
when 4 then pipeline.send(row[1], row[2], &row[3])
|
72
|
+
when 5 then pipeline.send(row[1], row[2], row[3], &row[4])
|
73
|
+
end
|
77
74
|
end
|
78
75
|
end
|
79
76
|
|
@@ -92,6 +89,14 @@ class RedisClient
|
|
92
89
|
|
93
90
|
raise ::RedisClient::Cluster::ErrorCollection, errors
|
94
91
|
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def add_line(node_key, line)
|
96
|
+
@grouped[node_key] = [] unless @grouped.key?(node_key)
|
97
|
+
@grouped[node_key] << line
|
98
|
+
@size += 1
|
99
|
+
end
|
95
100
|
end
|
96
101
|
end
|
97
102
|
end
|
@@ -6,6 +6,7 @@ require 'redis_client/cluster/errors'
|
|
6
6
|
require 'redis_client/cluster/key_slot_converter'
|
7
7
|
require 'redis_client/cluster/node'
|
8
8
|
require 'redis_client/cluster/node_key'
|
9
|
+
require 'redis_client/cluster/normalized_cmd_name'
|
9
10
|
|
10
11
|
class RedisClient
|
11
12
|
class Cluster
|
@@ -25,7 +26,7 @@ class RedisClient
|
|
25
26
|
end
|
26
27
|
|
27
28
|
def send_command(method, command, *args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
28
|
-
cmd =
|
29
|
+
cmd = ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_command(command)
|
29
30
|
case cmd
|
30
31
|
when 'acl', 'auth', 'bgrewriteaof', 'bgsave', 'quit', 'save'
|
31
32
|
@node.call_all(method, command, args, &block).first
|
@@ -65,7 +66,12 @@ class RedisClient
|
|
65
66
|
# @see https://redis.io/topics/cluster-spec#redirection-and-resharding
|
66
67
|
# Redirection and resharding
|
67
68
|
def try_send(node, method, command, args, retry_count: 3, &block) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
68
|
-
|
69
|
+
if args.empty?
|
70
|
+
# prevent memory allocation for variable-length args
|
71
|
+
node.send(method, command, &block)
|
72
|
+
else
|
73
|
+
node.send(method, *args, command, &block)
|
74
|
+
end
|
69
75
|
rescue ::RedisClient::CommandError => e
|
70
76
|
raise if retry_count <= 0
|
71
77
|
|
@@ -193,7 +199,7 @@ class RedisClient
|
|
193
199
|
end
|
194
200
|
|
195
201
|
def send_config_command(method, command, args, &block)
|
196
|
-
case
|
202
|
+
case ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_subcommand(command)
|
197
203
|
when 'resetstat', 'rewrite', 'set'
|
198
204
|
@node.call_all(method, command, args, &block).first
|
199
205
|
else assign_node(command).send(method, *args, command, &block)
|
@@ -201,7 +207,7 @@ class RedisClient
|
|
201
207
|
end
|
202
208
|
|
203
209
|
def send_memory_command(method, command, args, &block)
|
204
|
-
case
|
210
|
+
case ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_subcommand(command)
|
205
211
|
when 'stats' then @node.call_all(method, command, args, &block)
|
206
212
|
when 'purge' then @node.call_all(method, command, args, &block).first
|
207
213
|
else assign_node(command).send(method, *args, command, &block)
|
@@ -209,7 +215,7 @@ class RedisClient
|
|
209
215
|
end
|
210
216
|
|
211
217
|
def send_client_command(method, command, args, &block)
|
212
|
-
case
|
218
|
+
case ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_subcommand(command)
|
213
219
|
when 'list' then @node.call_all(method, command, args, &block).flatten
|
214
220
|
when 'pause', 'reply', 'setname'
|
215
221
|
@node.call_all(method, command, args, &block).first
|
@@ -217,10 +223,8 @@ class RedisClient
|
|
217
223
|
end
|
218
224
|
end
|
219
225
|
|
220
|
-
def send_cluster_command(method, command, args, &block)
|
221
|
-
subcommand =
|
222
|
-
|
223
|
-
case subcommand
|
226
|
+
def send_cluster_command(method, command, args, &block)
|
227
|
+
case subcommand = ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_subcommand(command)
|
224
228
|
when 'addslots', 'delslots', 'failover', 'forget', 'meet', 'replicate',
|
225
229
|
'reset', 'set-config-epoch', 'setslot'
|
226
230
|
raise ::RedisClient::Cluster::OrchestrationCommandNotSupported, ['cluster', subcommand]
|
@@ -234,7 +238,7 @@ class RedisClient
|
|
234
238
|
end
|
235
239
|
|
236
240
|
def send_script_command(method, command, args, &block)
|
237
|
-
case
|
241
|
+
case ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_subcommand(command)
|
238
242
|
when 'debug', 'kill'
|
239
243
|
@node.call_all(method, command, args, &block).first
|
240
244
|
when 'flush', 'load'
|
@@ -244,7 +248,7 @@ class RedisClient
|
|
244
248
|
end
|
245
249
|
|
246
250
|
def send_pubsub_command(method, command, args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
247
|
-
case
|
251
|
+
case ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_subcommand(command)
|
248
252
|
when 'channels' then @node.call_all(method, command, args, &block).flatten.uniq.sort_by(&:to_s)
|
249
253
|
when 'numsub'
|
250
254
|
@node.call_all(method, command, args, &block).reject(&:empty?).map { |e| Hash[*e] }
|
@@ -281,6 +285,8 @@ class RedisClient
|
|
281
285
|
end
|
282
286
|
|
283
287
|
def update_cluster_info!
|
288
|
+
return if @mutex.locked?
|
289
|
+
|
284
290
|
@mutex.synchronize do
|
285
291
|
begin
|
286
292
|
@node.each(&:close)
|
data/lib/redis_client/cluster.rb
CHANGED
@@ -78,7 +78,8 @@ class RedisClient
|
|
78
78
|
end
|
79
79
|
|
80
80
|
def pipelined
|
81
|
-
|
81
|
+
seed = @config.use_replica? && @config.replica_affinity == :random ? nil : Random.new_seed
|
82
|
+
pipeline = ::RedisClient::Cluster::Pipeline.new(@router, @command_builder, seed: seed)
|
82
83
|
yield pipeline
|
83
84
|
return [] if pipeline.empty? == 0
|
84
85
|
|
@@ -83,10 +83,14 @@ class RedisClient
|
|
83
83
|
end
|
84
84
|
|
85
85
|
def update_node(addrs)
|
86
|
+
return if @mutex.locked?
|
87
|
+
|
86
88
|
@mutex.synchronize { @node_configs = build_node_configs(addrs) }
|
87
89
|
end
|
88
90
|
|
89
91
|
def add_node(host, port)
|
92
|
+
return if @mutex.locked?
|
93
|
+
|
90
94
|
@mutex.synchronize { @node_configs << { host: host, port: port } }
|
91
95
|
end
|
92
96
|
|
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.3.
|
4
|
+
version: 0.3.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Taishi Kasuga
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-09-
|
11
|
+
date: 2022-09-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis-client
|
@@ -42,6 +42,7 @@ files:
|
|
42
42
|
- lib/redis_client/cluster/node/random_replica.rb
|
43
43
|
- lib/redis_client/cluster/node/replica_mixin.rb
|
44
44
|
- lib/redis_client/cluster/node_key.rb
|
45
|
+
- lib/redis_client/cluster/normalized_cmd_name.rb
|
45
46
|
- lib/redis_client/cluster/pipeline.rb
|
46
47
|
- lib/redis_client/cluster/pub_sub.rb
|
47
48
|
- lib/redis_client/cluster/router.rb
|