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: 158cfc3693b4057ae42249cbddcca2e5bd53bb19bb62416e44f1d1358b55c711
4
- data.tar.gz: 59917a893698b9d0fec7235821e4f9680b9e3ab4ebfd74554c48c50455a87186
3
+ metadata.gz: 5aa02a0e3934e8246e948260f2a4457b324de2377e7ec2e00fe9bf03a52f5ee4
4
+ data.tar.gz: 9f97a22af58eb64c64a6b73a79c2947bad06318e17bca235a5aa59fae35aad1d
5
5
  SHA512:
6
- metadata.gz: 116c7f397e557fc0b6a336e3481cc3e5458bac590afe454ecbfbfcafddc67ea3641f9cd020ab7b9f2cb41f3fe54188a13d4324b9496e43a27b1f58f97163363d
7
- data.tar.gz: e7ada90bdb11f0d113093792b627853a429d182c1e531d6c968fff530bbd34ed8df76655fd40223b91bc3c5d6524316302fa435438e731a8de1dcb0bc5c4ad24
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(keys, router)
11
- @node = find_node!(keys, router)
12
- @keys = keys
9
+ def initialize(router)
10
+ @router = router
13
11
  end
14
12
 
15
- def watch
16
- @node.with do |c|
17
- c.call('WATCH', *@keys)
18
- reply = yield(c)
19
- c.call('UNWATCH')
20
- reply
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 find_node!(keys, router)
27
- raise ::RedisClient::Cluster::Transaction::ConsistencyError, "unsafe watch: #{keys.join(' ')}" unless safe?(keys)
30
+ def find_slot(keys)
31
+ return if keys.empty?
32
+ return if keys.any? { |k| k.nil? || k.empty? }
28
33
 
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?
34
+ slots = keys.map { |k| @router.find_slot_by_key(k) }
35
+ return if slots.uniq.size != 1
31
36
 
32
- router.find_node(node_key)
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 = nil)
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: true)
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) if redirect
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', 'ASK')
145
- ensure_the_same_node!(commands)
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: false)
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: false) : err
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
@@ -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
 
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.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Taishi Kasuga