22
33require 'redis_client'
44require 'redis_client/cluster/pipeline'
5- require 'redis_client/cluster/key_slot_converter '
5+ require 'redis_client/cluster/node_key '
66
77class RedisClient
88 class Cluster
99 class Transaction
1010 ConsistencyError = Class . new ( ::RedisClient ::Error )
1111
12- def initialize ( router , command_builder , watch )
12+ def initialize ( router , command_builder , node = nil )
1313 @router = router
1414 @command_builder = command_builder
15- @watch = watch
1615 @retryable = true
1716 @pipeline = ::RedisClient ::Pipeline . new ( @command_builder )
1817 @pending_commands = [ ]
19- @node = nil
18+ @node = node
19+ prepare_tx unless @node . nil?
2020 end
2121
2222 def call ( *command , **kwargs , &block )
@@ -62,7 +62,6 @@ def execute
6262
6363 raise ArgumentError , 'empty transaction' if @pipeline . _empty?
6464 raise ConsistencyError , "couldn't determine the node: #{ @pipeline . _commands } " if @node . nil?
65- raise ConsistencyError , "unsafe watch: #{ @watch . join ( ' ' ) } " unless safe_watch?
6665
6766 settle
6867 end
@@ -74,42 +73,25 @@ def defer(&block)
7473 nil
7574 end
7675
77- def watch?
78- !@watch . nil? && !@watch . empty?
79- end
80-
81- def safe_watch?
82- return true unless watch?
83- return false if @node . nil?
84-
85- slots = @watch . map do |k |
86- return false if k . nil? || k . empty?
87-
88- ::RedisClient ::Cluster ::KeySlotConverter . convert ( k )
89- end
90-
91- return false if slots . uniq . size != 1
92-
93- @router . find_primary_node_by_slot ( slots . first ) == @node
94- end
95-
9676 def prepare ( command )
9777 return true unless @node . nil?
9878
9979 node_key = @router . find_primary_node_key ( command )
10080 return false if node_key . nil?
10181
10282 @node = @router . find_node ( node_key )
103- @pipeline . call ( 'WATCH' , *@watch ) if watch?
83+ prepare_tx
84+ true
85+ end
86+
87+ def prepare_tx
10488 @pipeline . call ( 'MULTI' )
10589 @pending_commands . each ( &:call )
10690 @pending_commands . clear
107- true
10891 end
10992
11093 def settle
11194 @pipeline . call ( 'EXEC' )
112- @pipeline . call ( 'UNWATCH' ) if watch?
11395 send_transaction ( @node , redirect : true )
11496 end
11597
@@ -133,11 +115,12 @@ def send_pipeline(client, redirect:)
133115 end
134116 end
135117
136- offset = watch? ? 2 : 1
137- coerce_results! ( replies [ -offset ] , offset )
118+ return if replies . last . nil?
119+
120+ coerce_results! ( replies . last )
138121 end
139122
140- def coerce_results! ( results , offset )
123+ def coerce_results! ( results , offset : 1 )
141124 results . each_with_index do |result , index |
142125 if result . is_a? ( ::RedisClient ::CommandError )
143126 result . _set_command ( @pipeline . _commands [ index + offset ] )
@@ -167,12 +150,12 @@ def handle_command_error!(commands, err)
167150 end
168151
169152 def ensure_the_same_node! ( commands )
153+ expected_node_key = NodeKey . build_from_client ( @node )
154+
170155 commands . each do |command |
171156 node_key = @router . find_primary_node_key ( command )
172157 next if node_key . nil?
173-
174- node = @router . find_node ( node_key )
175- next if @node == node
158+ next if node_key == expected_node_key
176159
177160 raise ConsistencyError , "the transaction should be executed to a slot in a node: #{ commands } "
178161 end
0 commit comments