redis-cluster-client 0.8.1 → 0.9.0
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 +6 -6
- data/lib/redis_client/cluster/node/base_topology.rb +3 -0
- data/lib/redis_client/cluster/node.rb +2 -7
- data/lib/redis_client/cluster/router.rb +42 -1
- data/lib/redis_client/cluster/transaction.rb +13 -3
- data/lib/redis_client/cluster.rb +2 -21
- metadata +3 -4
- data/lib/redis_client/cluster/pinning_node.rb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 97383b0daf1dcc2e5fcd8e50640e9ecbd903562a10cca0f4996ada1807469f80
|
4
|
+
data.tar.gz: 675ae25f2fbf2d9d67234b4e1ad81894656f11a32fe4cd1ebf8ae73c90c67509
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 851565ace738fb9bf51d1211ece97767520c94a7b02e49b053732de351d77a30e7104ae1f8143a0ee3903a2b99de39b298b3eec80c2fb94b08fb4747180bb03d
|
7
|
+
data.tar.gz: 0a0b6ada72bccfcdb7cecda533f8f31761f15f991e359bc211a4d54ba973b2c68983efd3a9a594e7baab11b614fecb058de98b71f1890f7e52150f40e2c34d1b
|
@@ -97,6 +97,12 @@ 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
|
+
|
100
106
|
private
|
101
107
|
|
102
108
|
def determine_first_key_position(command) # rubocop:disable Metrics/CyclomaticComplexity
|
@@ -143,12 +149,6 @@ class RedisClient
|
|
143
149
|
end
|
144
150
|
end
|
145
151
|
|
146
|
-
def determine_key_step(command)
|
147
|
-
name = ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_command(command)
|
148
|
-
# Some commands like EVALSHA have zero as the step in COMMANDS somehow.
|
149
|
-
@commands[name].key_step == 0 ? 1 : @commands[name].key_step
|
150
|
-
end
|
151
|
-
|
152
152
|
def determine_optional_key_position(command, option_name) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
153
153
|
idx = command&.flatten&.map(&:to_s)&.map(&:downcase)&.index(option_name&.downcase)
|
154
154
|
idx.nil? ? 0 : idx + 1
|
@@ -5,6 +5,9 @@ class RedisClient
|
|
5
5
|
class Node
|
6
6
|
class BaseTopology
|
7
7
|
IGNORE_GENERIC_CONFIG_KEYS = %i[url host port path].freeze
|
8
|
+
EMPTY_HASH = {}.freeze
|
9
|
+
EMPTY_ARRAY = [].freeze
|
10
|
+
|
8
11
|
attr_reader :clients, :primary_clients, :replica_clients
|
9
12
|
|
10
13
|
def initialize(pool, concurrent_worker, **kwargs)
|
@@ -92,13 +92,7 @@ class RedisClient
|
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
95
|
-
def initialize(
|
96
|
-
concurrent_worker,
|
97
|
-
config:,
|
98
|
-
pool: nil,
|
99
|
-
**kwargs
|
100
|
-
)
|
101
|
-
|
95
|
+
def initialize(concurrent_worker, config:, pool: nil, **kwargs)
|
102
96
|
@concurrent_worker = concurrent_worker
|
103
97
|
@slots = build_slot_node_mappings(EMPTY_ARRAY)
|
104
98
|
@replications = build_replication_mappings(EMPTY_ARRAY)
|
@@ -106,6 +100,7 @@ class RedisClient
|
|
106
100
|
@topology = klass.new(pool, @concurrent_worker, **kwargs)
|
107
101
|
@config = config
|
108
102
|
@mutex = Mutex.new
|
103
|
+
@last_reloaded_at = nil
|
109
104
|
end
|
110
105
|
|
111
106
|
def inspect
|
@@ -10,6 +10,7 @@ require 'redis_client/cluster/node_key'
|
|
10
10
|
require 'redis_client/cluster/normalized_cmd_name'
|
11
11
|
require 'redis_client/cluster/transaction'
|
12
12
|
require 'redis_client/cluster/optimistic_locking'
|
13
|
+
require 'redis_client/cluster/pipeline'
|
13
14
|
require 'redis_client/cluster/error_identification'
|
14
15
|
|
15
16
|
class RedisClient
|
@@ -17,6 +18,11 @@ class RedisClient
|
|
17
18
|
class Router
|
18
19
|
ZERO_CURSOR_FOR_SCAN = '0'
|
19
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
|
20
26
|
|
21
27
|
def initialize(config, concurrent_worker, pool: nil, **kwargs)
|
22
28
|
@config = config.dup
|
@@ -48,6 +54,8 @@ class RedisClient
|
|
48
54
|
when 'script' then send_script_command(method, command, args, &block)
|
49
55
|
when 'pubsub' then send_pubsub_command(method, command, args, &block)
|
50
56
|
when 'watch' then send_watch_command(command, &block)
|
57
|
+
when 'mset', 'mget', 'del'
|
58
|
+
send_multiple_keys_command(cmd, method, command, args, &block)
|
51
59
|
when 'acl', 'auth', 'bgrewriteaof', 'bgsave', 'quit', 'save'
|
52
60
|
@node.call_all(method, command, args).first.then(&TSF.call(block))
|
53
61
|
when 'flushall', 'flushdb'
|
@@ -314,7 +322,6 @@ class RedisClient
|
|
314
322
|
end
|
315
323
|
end
|
316
324
|
|
317
|
-
# for redis-rb
|
318
325
|
def send_watch_command(command)
|
319
326
|
raise ::RedisClient::Cluster::Transaction::ConsistencyError, 'A block required. And you need to use the block argument as a client for the transaction.' unless block_given?
|
320
327
|
|
@@ -327,6 +334,40 @@ class RedisClient
|
|
327
334
|
end
|
328
335
|
end
|
329
336
|
|
337
|
+
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.extract_hash_tag(command[1]).empty? # rubocop:disable Style/IfUnlessModifier
|
340
|
+
return try_send(assign_node(command), method, command, args, &block)
|
341
|
+
end
|
342
|
+
|
343
|
+
seed = @config.use_replica? && @config.replica_affinity == :random ? nil : Random.new_seed
|
344
|
+
pipeline = ::RedisClient::Cluster::Pipeline.new(self, @command_builder, @concurrent_worker, exception: true, seed: seed)
|
345
|
+
|
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)
|
349
|
+
single_command[0] = single_key_cmd
|
350
|
+
if key_step == 1
|
351
|
+
command[1..].each do |key|
|
352
|
+
single_command[1] = key
|
353
|
+
pipeline.call_v(single_command)
|
354
|
+
end
|
355
|
+
else
|
356
|
+
command[1..].each_slice(key_step) do |v|
|
357
|
+
key_step.times { |i| single_command[i + 1] = v[i] }
|
358
|
+
pipeline.call_v(single_command)
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
replies = pipeline.execute
|
363
|
+
result = case cmd
|
364
|
+
when 'mset' then replies.first
|
365
|
+
when 'del' then replies.sum
|
366
|
+
else replies
|
367
|
+
end
|
368
|
+
block_given? ? yield(result) : result
|
369
|
+
end
|
370
|
+
|
330
371
|
def update_cluster_info!
|
331
372
|
@node.reload!
|
332
373
|
end
|
@@ -8,6 +8,7 @@ class RedisClient
|
|
8
8
|
class Transaction
|
9
9
|
ConsistencyError = Class.new(::RedisClient::Error)
|
10
10
|
MAX_REDIRECTION = 2
|
11
|
+
EMPTY_ARRAY = [].freeze
|
11
12
|
|
12
13
|
def initialize(router, command_builder, node: nil, slot: nil, asking: false)
|
13
14
|
@router = router
|
@@ -62,10 +63,10 @@ class RedisClient
|
|
62
63
|
def execute
|
63
64
|
@pending_commands.each(&:call)
|
64
65
|
|
65
|
-
|
66
|
+
return EMPTY_ARRAY if @pipeline._empty?
|
66
67
|
raise ConsistencyError, "couldn't determine the node: #{@pipeline._commands}" if @node.nil?
|
67
68
|
|
68
|
-
|
69
|
+
commit
|
69
70
|
end
|
70
71
|
|
71
72
|
private
|
@@ -92,8 +93,17 @@ class RedisClient
|
|
92
93
|
@pending_commands.clear
|
93
94
|
end
|
94
95
|
|
95
|
-
def
|
96
|
+
def commit
|
96
97
|
@pipeline.call('EXEC')
|
98
|
+
settle
|
99
|
+
end
|
100
|
+
|
101
|
+
def cancel
|
102
|
+
@pipeline.call('DISCARD')
|
103
|
+
settle
|
104
|
+
end
|
105
|
+
|
106
|
+
def settle
|
97
107
|
# If we needed ASKING on the watch, we need ASKING on the multi as well.
|
98
108
|
@node.call('ASKING') if @asking
|
99
109
|
# Don't handle redirections at this level if we're in a watch (the watcher handles redirections
|
data/lib/redis_client/cluster.rb
CHANGED
@@ -5,7 +5,6 @@ require 'redis_client/cluster/pipeline'
|
|
5
5
|
require 'redis_client/cluster/pub_sub'
|
6
6
|
require 'redis_client/cluster/router'
|
7
7
|
require 'redis_client/cluster/transaction'
|
8
|
-
require 'redis_client/cluster/pinning_node'
|
9
8
|
require 'redis_client/cluster/optimistic_locking'
|
10
9
|
|
11
10
|
class RedisClient
|
@@ -118,13 +117,8 @@ class RedisClient
|
|
118
117
|
::RedisClient::Cluster::PubSub.new(@router, @command_builder)
|
119
118
|
end
|
120
119
|
|
121
|
-
|
122
|
-
|
123
|
-
def with(key: nil, hashtag: nil, write: true)
|
124
|
-
key = process_with_arguments(key, hashtag)
|
125
|
-
node_key = @router.find_node_key_by_key(key, primary: write)
|
126
|
-
node = @router.find_node(node_key)
|
127
|
-
node.with { |c| yield ::RedisClient::Cluster::PinningNode.new(c) }
|
120
|
+
def with(...)
|
121
|
+
raise NotImplementedError, 'No way to use'
|
128
122
|
end
|
129
123
|
|
130
124
|
def close
|
@@ -135,19 +129,6 @@ class RedisClient
|
|
135
129
|
|
136
130
|
private
|
137
131
|
|
138
|
-
def process_with_arguments(key, hashtag) # rubocop:disable Metrics/CyclomaticComplexity
|
139
|
-
raise ArgumentError, 'Only one of key or hashtag may be provided' if key && hashtag
|
140
|
-
|
141
|
-
if hashtag
|
142
|
-
# The documentation says not to wrap your hashtag in {}, but people will probably
|
143
|
-
# do it anyway and it's easy for us to fix here.
|
144
|
-
key = hashtag&.match?(/^{.*}$/) ? hashtag : "{#{hashtag}}"
|
145
|
-
end
|
146
|
-
raise ArgumentError, 'One of key or hashtag must be provided' if key.nil? || key.empty?
|
147
|
-
|
148
|
-
key
|
149
|
-
end
|
150
|
-
|
151
132
|
def method_missing(name, *args, **kwargs, &block)
|
152
133
|
if @router.command_exists?(name)
|
153
134
|
args.unshift(name)
|
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.
|
4
|
+
version: 0.9.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-
|
11
|
+
date: 2024-05-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis-client
|
@@ -50,7 +50,6 @@ files:
|
|
50
50
|
- lib/redis_client/cluster/node_key.rb
|
51
51
|
- lib/redis_client/cluster/normalized_cmd_name.rb
|
52
52
|
- lib/redis_client/cluster/optimistic_locking.rb
|
53
|
-
- lib/redis_client/cluster/pinning_node.rb
|
54
53
|
- lib/redis_client/cluster/pipeline.rb
|
55
54
|
- lib/redis_client/cluster/pub_sub.rb
|
56
55
|
- lib/redis_client/cluster/router.rb
|
@@ -78,7 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
78
77
|
- !ruby/object:Gem::Version
|
79
78
|
version: '0'
|
80
79
|
requirements: []
|
81
|
-
rubygems_version: 3.5.
|
80
|
+
rubygems_version: 3.5.9
|
82
81
|
signing_key:
|
83
82
|
specification_version: 4
|
84
83
|
summary: A Redis cluster client for Ruby
|
@@ -1,35 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class RedisClient
|
4
|
-
class Cluster
|
5
|
-
class PinningNode
|
6
|
-
def initialize(client)
|
7
|
-
@client = client
|
8
|
-
end
|
9
|
-
|
10
|
-
def call(*args, **kwargs, &block)
|
11
|
-
@client.call(*args, **kwargs, &block)
|
12
|
-
end
|
13
|
-
|
14
|
-
def call_v(args, &block)
|
15
|
-
@client.call_v(args, &block)
|
16
|
-
end
|
17
|
-
|
18
|
-
def call_once(*args, **kwargs, &block)
|
19
|
-
@client.call_once(*args, **kwargs, &block)
|
20
|
-
end
|
21
|
-
|
22
|
-
def call_once_v(args, &block)
|
23
|
-
@client.call_once_v(args, &block)
|
24
|
-
end
|
25
|
-
|
26
|
-
def blocking_call(timeout, *args, **kwargs, &block)
|
27
|
-
@client.blocking_call(timeout, *args, **kwargs, &block)
|
28
|
-
end
|
29
|
-
|
30
|
-
def blocking_call_v(timeout, args, &block)
|
31
|
-
@client.blocking_call_v(timeout, args, &block)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|