redis-cluster-client 0.7.7 → 0.7.8
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/optimistic_locking.rb +25 -15
- data/lib/redis_client/cluster/transaction.rb +20 -27
- data/lib/redis_client/cluster.rb +8 -7
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 41d87ce45a108ff8ec7d3c17805276d5ba7df11d577de3f7d6b1176bdd297ed0
|
4
|
+
data.tar.gz: 7118539907bb755225ba908cf160ad8af73aeb5204820682ca3fc3a67813f3cb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 96e3c0f9c2860002642e079b756065e32f04891cd793dce9efcb74bb87a13683b14fee13c039693d99599549fee7ace68d54561b8c730d5e9eb7bb937a82c57d
|
7
|
+
data.tar.gz: 2e512b774b5778e3f7a2a3557bb45d97971d209c787494a19d81e0d8146f5d51bbef7940f37fa38f7587e1037185e23fa27ca8d48a46225ced6640be8425198e
|
@@ -7,29 +7,32 @@ require 'redis_client/cluster/transaction'
|
|
7
7
|
class RedisClient
|
8
8
|
class Cluster
|
9
9
|
class OptimisticLocking
|
10
|
-
def initialize(
|
11
|
-
@
|
12
|
-
@keys = keys
|
10
|
+
def initialize(router)
|
11
|
+
@router = router
|
13
12
|
end
|
14
13
|
|
15
|
-
def watch
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
def watch(keys)
|
15
|
+
ensure_safe_keys(keys)
|
16
|
+
node = find_node(keys)
|
17
|
+
cnt = 0 # We assume redirects occurred when incrementing it.
|
18
|
+
|
19
|
+
@router.handle_redirection(node, retry_count: 1) do |nd|
|
20
|
+
cnt += 1
|
21
|
+
nd.with do |c|
|
22
|
+
c.call('WATCH', *keys)
|
23
|
+
reply = yield(c, cnt > 1)
|
24
|
+
c.call('UNWATCH')
|
25
|
+
reply
|
26
|
+
end
|
21
27
|
end
|
22
28
|
end
|
23
29
|
|
24
30
|
private
|
25
31
|
|
26
|
-
def
|
27
|
-
|
32
|
+
def ensure_safe_keys(keys)
|
33
|
+
return if safe?(keys)
|
28
34
|
|
29
|
-
|
30
|
-
raise ::RedisClient::Cluster::Transaction::ConsistencyError, "couldn't determine the node" if node_key.nil?
|
31
|
-
|
32
|
-
router.find_node(node_key)
|
35
|
+
raise ::RedisClient::Cluster::Transaction::ConsistencyError, "unsafe watch: #{keys.join(' ')}"
|
33
36
|
end
|
34
37
|
|
35
38
|
def safe?(keys)
|
@@ -43,6 +46,13 @@ class RedisClient
|
|
43
46
|
|
44
47
|
slots.uniq.size == 1
|
45
48
|
end
|
49
|
+
|
50
|
+
def find_node(keys)
|
51
|
+
node_key = @router.find_primary_node_key(['WATCH', *keys])
|
52
|
+
return @router.find_node(node_key) unless node_key.nil?
|
53
|
+
|
54
|
+
raise ::RedisClient::Cluster::Transaction::ConsistencyError, "couldn't determine the node"
|
55
|
+
end
|
46
56
|
end
|
47
57
|
end
|
48
58
|
end
|
@@ -8,8 +8,9 @@ class RedisClient
|
|
8
8
|
class Cluster
|
9
9
|
class Transaction
|
10
10
|
ConsistencyError = Class.new(::RedisClient::Error)
|
11
|
+
MAX_REDIRECTION = 2
|
11
12
|
|
12
|
-
def initialize(router, command_builder, node
|
13
|
+
def initialize(router, command_builder, node: nil, resharding: false)
|
13
14
|
@router = router
|
14
15
|
@command_builder = command_builder
|
15
16
|
@retryable = true
|
@@ -17,6 +18,7 @@ class RedisClient
|
|
17
18
|
@pending_commands = []
|
18
19
|
@node = node
|
19
20
|
prepare_tx unless @node.nil?
|
21
|
+
@resharding_state = resharding
|
20
22
|
end
|
21
23
|
|
22
24
|
def call(*command, **kwargs, &block)
|
@@ -92,7 +94,7 @@ class RedisClient
|
|
92
94
|
|
93
95
|
def settle
|
94
96
|
@pipeline.call('EXEC')
|
95
|
-
send_transaction(@node, redirect:
|
97
|
+
send_transaction(@node, redirect: MAX_REDIRECTION)
|
96
98
|
end
|
97
99
|
|
98
100
|
def send_transaction(client, redirect:)
|
@@ -109,7 +111,7 @@ class RedisClient
|
|
109
111
|
client.middlewares.call_pipelined(commands, client.config) do
|
110
112
|
connection.call_pipelined(commands, nil)
|
111
113
|
rescue ::RedisClient::CommandError => e
|
112
|
-
return handle_command_error!(commands, e)
|
114
|
+
return handle_command_error!(client, commands, e, redirect: redirect) unless redirect.zero?
|
113
115
|
|
114
116
|
raise
|
115
117
|
end
|
@@ -138,39 +140,30 @@ class RedisClient
|
|
138
140
|
results
|
139
141
|
end
|
140
142
|
|
141
|
-
def handle_command_error!(commands, err)
|
143
|
+
def handle_command_error!(client, commands, err, redirect:) # rubocop:disable Metrics/AbcSize
|
142
144
|
if err.message.start_with?('CROSSSLOT')
|
143
145
|
raise ConsistencyError, "#{err.message}: #{err.command}"
|
144
|
-
elsif err.message.start_with?('MOVED'
|
145
|
-
ensure_the_same_node!(commands)
|
146
|
-
|
146
|
+
elsif err.message.start_with?('MOVED')
|
147
|
+
ensure_the_same_node!(client, commands)
|
148
|
+
node = @router.assign_redirection_node(err.message)
|
149
|
+
send_transaction(node, redirect: redirect - 1)
|
150
|
+
elsif err.message.start_with?('ASK')
|
151
|
+
ensure_the_same_node!(client, commands)
|
152
|
+
node = @router.assign_asking_node(err.message)
|
153
|
+
try_asking(node) ? send_transaction(node, redirect: redirect - 1) : err
|
147
154
|
else
|
148
155
|
raise err
|
149
156
|
end
|
150
157
|
end
|
151
158
|
|
152
|
-
def ensure_the_same_node!(commands)
|
153
|
-
|
159
|
+
def ensure_the_same_node!(client, commands)
|
160
|
+
node_keys = commands.map { |command| @router.find_primary_node_key(command) }.compact.uniq
|
161
|
+
expected_node_key = ::RedisClient::Cluster::NodeKey.build_from_client(client)
|
154
162
|
|
155
|
-
|
156
|
-
|
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
|
+
return if !@resharding_state && node_keys.size == 1 && node_keys.first == expected_node_key
|
164
|
+
return if @resharding_state && node_keys.size == 1
|
163
165
|
|
164
|
-
|
165
|
-
if err.message.start_with?('MOVED')
|
166
|
-
node = @router.assign_redirection_node(err.message)
|
167
|
-
send_transaction(node, redirect: false)
|
168
|
-
elsif err.message.start_with?('ASK')
|
169
|
-
node = @router.assign_asking_node(err.message)
|
170
|
-
try_asking(node) ? send_transaction(node, redirect: false) : err
|
171
|
-
else
|
172
|
-
raise err
|
173
|
-
end
|
166
|
+
raise(ConsistencyError, "the transaction should be executed to a slot in a node: #{commands}")
|
174
167
|
end
|
175
168
|
|
176
169
|
def try_asking(node)
|
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, resharding|
|
102
|
+
transaction = ::RedisClient::Cluster::Transaction.new(
|
103
|
+
@router, @command_builder, node: c, resharding: resharding
|
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
|
|