redis-cluster-client 0.7.7 → 0.7.9
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
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5aa02a0e3934e8246e948260f2a4457b324de2377e7ec2e00fe9bf03a52f5ee4
|
4
|
+
data.tar.gz: 9f97a22af58eb64c64a6b73a79c2947bad06318e17bca235a5aa59fae35aad1d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2e8fcf1be08ba5c2582ad112455b02815410a81834b55bfc0035e0971556ff7b443e60a48a4cc2f5c94fdd81d09a5d03d3c261b4e0afb13ac9f38da5d4f0ab64
|
7
|
+
data.tar.gz: 77f17d9e003384f34e5719f0e84f70c8a04f7de196974fc5af830e4825827b01d7df94b4eb887d4a1a4a77f43caa989cfdcbb1e874d807503bf9ef254ea95a0d
|
@@ -1,47 +1,40 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'redis_client'
|
4
|
-
require 'redis_client/cluster/key_slot_converter'
|
5
4
|
require 'redis_client/cluster/transaction'
|
6
5
|
|
7
6
|
class RedisClient
|
8
7
|
class Cluster
|
9
8
|
class OptimisticLocking
|
10
|
-
def initialize(
|
11
|
-
@
|
12
|
-
@keys = keys
|
9
|
+
def initialize(router)
|
10
|
+
@router = router
|
13
11
|
end
|
14
12
|
|
15
|
-
def watch
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
13
|
+
def watch(keys)
|
14
|
+
slot = find_slot(keys)
|
15
|
+
raise ::RedisClient::Cluster::Transaction::ConsistencyError, "unsafe watch: #{keys.join(' ')}" if slot.nil?
|
16
|
+
|
17
|
+
node = @router.find_primary_node_by_slot(slot)
|
18
|
+
@router.handle_redirection(node, retry_count: 1) do |nd|
|
19
|
+
nd.with do |c|
|
20
|
+
c.call('WATCH', *keys)
|
21
|
+
reply = yield(c, slot)
|
22
|
+
c.call('UNWATCH')
|
23
|
+
reply
|
24
|
+
end
|
21
25
|
end
|
22
26
|
end
|
23
27
|
|
24
28
|
private
|
25
29
|
|
26
|
-
def
|
27
|
-
|
30
|
+
def find_slot(keys)
|
31
|
+
return if keys.empty?
|
32
|
+
return if keys.any? { |k| k.nil? || k.empty? }
|
28
33
|
|
29
|
-
|
30
|
-
|
34
|
+
slots = keys.map { |k| @router.find_slot_by_key(k) }
|
35
|
+
return if slots.uniq.size != 1
|
31
36
|
|
32
|
-
|
33
|
-
end
|
34
|
-
|
35
|
-
def safe?(keys)
|
36
|
-
return false if keys.empty?
|
37
|
-
|
38
|
-
slots = keys.map do |k|
|
39
|
-
return false if k.nil? || k.empty?
|
40
|
-
|
41
|
-
::RedisClient::Cluster::KeySlotConverter.convert(k)
|
42
|
-
end
|
43
|
-
|
44
|
-
slots.uniq.size == 1
|
37
|
+
slots.first
|
45
38
|
end
|
46
39
|
end
|
47
40
|
end
|
@@ -179,6 +179,16 @@ class RedisClient
|
|
179
179
|
find_node_key_by_key(key, primary: true)
|
180
180
|
end
|
181
181
|
|
182
|
+
def find_slot(command)
|
183
|
+
find_slot_by_key(@command.extract_first_key(command))
|
184
|
+
end
|
185
|
+
|
186
|
+
def find_slot_by_key(key)
|
187
|
+
return if key.empty?
|
188
|
+
|
189
|
+
::RedisClient::Cluster::KeySlotConverter.convert(key)
|
190
|
+
end
|
191
|
+
|
182
192
|
def find_node(node_key, retry_count: 3)
|
183
193
|
@node.find_by(node_key)
|
184
194
|
rescue ::RedisClient::Cluster::Node::ReloadNeeded
|
@@ -2,14 +2,14 @@
|
|
2
2
|
|
3
3
|
require 'redis_client'
|
4
4
|
require 'redis_client/cluster/pipeline'
|
5
|
-
require 'redis_client/cluster/node_key'
|
6
5
|
|
7
6
|
class RedisClient
|
8
7
|
class Cluster
|
9
8
|
class Transaction
|
10
9
|
ConsistencyError = Class.new(::RedisClient::Error)
|
10
|
+
MAX_REDIRECTION = 2
|
11
11
|
|
12
|
-
def initialize(router, command_builder, node
|
12
|
+
def initialize(router, command_builder, node: nil, slot: nil)
|
13
13
|
@router = router
|
14
14
|
@command_builder = command_builder
|
15
15
|
@retryable = true
|
@@ -17,6 +17,7 @@ class RedisClient
|
|
17
17
|
@pending_commands = []
|
18
18
|
@node = node
|
19
19
|
prepare_tx unless @node.nil?
|
20
|
+
@watching_slot = slot
|
20
21
|
end
|
21
22
|
|
22
23
|
def call(*command, **kwargs, &block)
|
@@ -92,7 +93,7 @@ class RedisClient
|
|
92
93
|
|
93
94
|
def settle
|
94
95
|
@pipeline.call('EXEC')
|
95
|
-
send_transaction(@node, redirect:
|
96
|
+
send_transaction(@node, redirect: MAX_REDIRECTION)
|
96
97
|
end
|
97
98
|
|
98
99
|
def send_transaction(client, redirect:)
|
@@ -109,7 +110,7 @@ class RedisClient
|
|
109
110
|
client.middlewares.call_pipelined(commands, client.config) do
|
110
111
|
connection.call_pipelined(commands, nil)
|
111
112
|
rescue ::RedisClient::CommandError => e
|
112
|
-
return handle_command_error!(commands, e)
|
113
|
+
return handle_command_error!(commands, e, redirect: redirect) unless redirect.zero?
|
113
114
|
|
114
115
|
raise
|
115
116
|
end
|
@@ -138,41 +139,30 @@ class RedisClient
|
|
138
139
|
results
|
139
140
|
end
|
140
141
|
|
141
|
-
def handle_command_error!(commands, err)
|
142
|
+
def handle_command_error!(commands, err, redirect:) # rubocop:disable Metrics/AbcSize
|
142
143
|
if err.message.start_with?('CROSSSLOT')
|
143
144
|
raise ConsistencyError, "#{err.message}: #{err.command}"
|
144
|
-
elsif err.message.start_with?('MOVED'
|
145
|
-
|
146
|
-
handle_redirection(err)
|
147
|
-
else
|
148
|
-
raise err
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
def ensure_the_same_node!(commands)
|
153
|
-
expected_node_key = NodeKey.build_from_client(@node)
|
154
|
-
|
155
|
-
commands.each do |command|
|
156
|
-
node_key = @router.find_primary_node_key(command)
|
157
|
-
next if node_key.nil?
|
158
|
-
next if node_key == expected_node_key
|
159
|
-
|
160
|
-
raise ConsistencyError, "the transaction should be executed to a slot in a node: #{commands}"
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
def handle_redirection(err)
|
165
|
-
if err.message.start_with?('MOVED')
|
145
|
+
elsif err.message.start_with?('MOVED')
|
146
|
+
ensure_the_same_slot!(commands)
|
166
147
|
node = @router.assign_redirection_node(err.message)
|
167
|
-
send_transaction(node, redirect:
|
148
|
+
send_transaction(node, redirect: redirect - 1)
|
168
149
|
elsif err.message.start_with?('ASK')
|
150
|
+
ensure_the_same_slot!(commands)
|
169
151
|
node = @router.assign_asking_node(err.message)
|
170
|
-
try_asking(node) ? send_transaction(node, redirect:
|
152
|
+
try_asking(node) ? send_transaction(node, redirect: redirect - 1) : err
|
171
153
|
else
|
172
154
|
raise err
|
173
155
|
end
|
174
156
|
end
|
175
157
|
|
158
|
+
def ensure_the_same_slot!(commands)
|
159
|
+
slots = commands.map { |command| @router.find_slot(command) }.compact.uniq
|
160
|
+
return if slots.size == 1 && @watching_slot.nil?
|
161
|
+
return if slots.size == 1 && @watching_slot == slots.first
|
162
|
+
|
163
|
+
raise(ConsistencyError, "the transaction should be executed to a slot in a node: #{commands}")
|
164
|
+
end
|
165
|
+
|
176
166
|
def try_asking(node)
|
177
167
|
node.call('ASKING') == 'OK'
|
178
168
|
rescue StandardError
|
data/lib/redis_client/cluster.rb
CHANGED
@@ -95,14 +95,15 @@ class RedisClient
|
|
95
95
|
if watch.nil? || watch.empty?
|
96
96
|
transaction = ::RedisClient::Cluster::Transaction.new(@router, @command_builder)
|
97
97
|
yield transaction
|
98
|
+
return transaction.execute
|
99
|
+
end
|
100
|
+
|
101
|
+
::RedisClient::Cluster::OptimisticLocking.new(@router).watch(watch) do |c, slot|
|
102
|
+
transaction = ::RedisClient::Cluster::Transaction.new(
|
103
|
+
@router, @command_builder, node: c, slot: slot
|
104
|
+
)
|
105
|
+
yield transaction
|
98
106
|
transaction.execute
|
99
|
-
else
|
100
|
-
locking = ::RedisClient::Cluster::OptimisticLocking.new(watch, @router)
|
101
|
-
locking.watch do |c|
|
102
|
-
transaction = ::RedisClient::Cluster::Transaction.new(@router, @command_builder, c)
|
103
|
-
yield transaction
|
104
|
-
transaction.execute
|
105
|
-
end
|
106
107
|
end
|
107
108
|
end
|
108
109
|
|