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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 158cfc3693b4057ae42249cbddcca2e5bd53bb19bb62416e44f1d1358b55c711
4
- data.tar.gz: 59917a893698b9d0fec7235821e4f9680b9e3ab4ebfd74554c48c50455a87186
3
+ metadata.gz: 41d87ce45a108ff8ec7d3c17805276d5ba7df11d577de3f7d6b1176bdd297ed0
4
+ data.tar.gz: 7118539907bb755225ba908cf160ad8af73aeb5204820682ca3fc3a67813f3cb
5
5
  SHA512:
6
- metadata.gz: 116c7f397e557fc0b6a336e3481cc3e5458bac590afe454ecbfbfcafddc67ea3641f9cd020ab7b9f2cb41f3fe54188a13d4324b9496e43a27b1f58f97163363d
7
- data.tar.gz: e7ada90bdb11f0d113093792b627853a429d182c1e531d6c968fff530bbd34ed8df76655fd40223b91bc3c5d6524316302fa435438e731a8de1dcb0bc5c4ad24
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(keys, router)
11
- @node = find_node!(keys, router)
12
- @keys = keys
10
+ def initialize(router)
11
+ @router = router
13
12
  end
14
13
 
15
- def watch
16
- @node.with do |c|
17
- c.call('WATCH', *@keys)
18
- reply = yield(c)
19
- c.call('UNWATCH')
20
- reply
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 find_node!(keys, router)
27
- raise ::RedisClient::Cluster::Transaction::ConsistencyError, "unsafe watch: #{keys.join(' ')}" unless safe?(keys)
32
+ def ensure_safe_keys(keys)
33
+ return if safe?(keys)
28
34
 
29
- node_key = router.find_primary_node_key(['WATCH', *keys])
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 = nil)
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: true)
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) if redirect
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', 'ASK')
145
- ensure_the_same_node!(commands)
146
- handle_redirection(err)
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
- expected_node_key = NodeKey.build_from_client(@node)
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
- 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
+ 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
- def handle_redirection(err)
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)
@@ -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
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-cluster-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.7
4
+ version: 0.7.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Taishi Kasuga