redis-cluster-client 0.7.7 → 0.7.8

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: 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