2
2
3
3
require 'redis_client'
4
4
require 'redis_client/cluster/pipeline'
5
- require 'redis_client/cluster/key_slot_converter '
5
+ require 'redis_client/cluster/node_key '
6
6
7
7
class RedisClient
8
8
class Cluster
9
9
class Transaction
10
10
ConsistencyError = Class . new ( ::RedisClient ::Error )
11
11
12
- def initialize ( router , command_builder , watch )
12
+ def initialize ( router , command_builder , node = nil )
13
13
@router = router
14
14
@command_builder = command_builder
15
- @watch = watch
16
15
@retryable = true
17
16
@pipeline = ::RedisClient ::Pipeline . new ( @command_builder )
18
17
@pending_commands = [ ]
19
- @node = nil
18
+ @node = node
19
+ prepare_tx unless @node . nil?
20
20
end
21
21
22
22
def call ( *command , **kwargs , &block )
@@ -62,7 +62,6 @@ def execute
62
62
63
63
raise ArgumentError , 'empty transaction' if @pipeline . _empty?
64
64
raise ConsistencyError , "couldn't determine the node: #{ @pipeline . _commands } " if @node . nil?
65
- raise ConsistencyError , "unsafe watch: #{ @watch . join ( ' ' ) } " unless safe_watch?
66
65
67
66
settle
68
67
end
@@ -74,42 +73,25 @@ def defer(&block)
74
73
nil
75
74
end
76
75
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
-
96
76
def prepare ( command )
97
77
return true unless @node . nil?
98
78
99
79
node_key = @router . find_primary_node_key ( command )
100
80
return false if node_key . nil?
101
81
102
82
@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
104
88
@pipeline . call ( 'MULTI' )
105
89
@pending_commands . each ( &:call )
106
90
@pending_commands . clear
107
- true
108
91
end
109
92
110
93
def settle
111
94
@pipeline . call ( 'EXEC' )
112
- @pipeline . call ( 'UNWATCH' ) if watch?
113
95
send_transaction ( @node , redirect : true )
114
96
end
115
97
@@ -133,11 +115,12 @@ def send_pipeline(client, redirect:)
133
115
end
134
116
end
135
117
136
- offset = watch? ? 2 : 1
137
- coerce_results! ( replies [ -offset ] , offset )
118
+ return if replies . last . nil?
119
+
120
+ coerce_results! ( replies . last )
138
121
end
139
122
140
- def coerce_results! ( results , offset )
123
+ def coerce_results! ( results , offset : 1 )
141
124
results . each_with_index do |result , index |
142
125
if result . is_a? ( ::RedisClient ::CommandError )
143
126
result . _set_command ( @pipeline . _commands [ index + offset ] )
@@ -167,12 +150,12 @@ def handle_command_error!(commands, err)
167
150
end
168
151
169
152
def ensure_the_same_node! ( commands )
153
+ expected_node_key = NodeKey . build_from_client ( @node )
154
+
170
155
commands . each do |command |
171
156
node_key = @router . find_primary_node_key ( command )
172
157
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
176
159
177
160
raise ConsistencyError , "the transaction should be executed to a slot in a node: #{ commands } "
178
161
end
0 commit comments