lhm-shopify 3.5.1 → 3.5.5
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 +4 -4
- data/CHANGELOG.md +26 -0
- data/Gemfile.lock +1 -1
- data/README.md +14 -3
- data/Rakefile +6 -6
- data/dev.yml +5 -2
- data/gemfiles/activerecord_5.2.gemfile.lock +1 -1
- data/gemfiles/activerecord_6.0.gemfile.lock +1 -1
- data/gemfiles/activerecord_6.1.gemfile.lock +1 -1
- data/gemfiles/activerecord_7.0.0.alpha2.gemfile.lock +1 -1
- data/lib/lhm/atomic_switcher.rb +4 -3
- data/lib/lhm/chunk_insert.rb +6 -3
- data/lib/lhm/chunker.rb +6 -6
- data/lib/lhm/cleanup/current.rb +4 -1
- data/lib/lhm/connection.rb +33 -25
- data/lib/lhm/entangler.rb +5 -4
- data/lib/lhm/invoker.rb +5 -3
- data/lib/lhm/locked_switcher.rb +2 -0
- data/lib/lhm/proxysql_helper.rb +1 -1
- data/lib/lhm/sql_retry.rb +56 -60
- data/lib/lhm/version.rb +1 -1
- data/lib/lhm.rb +30 -24
- data/spec/integration/atomic_switcher_spec.rb +28 -17
- data/spec/integration/chunker_spec.rb +7 -5
- data/spec/integration/integration_helper.rb +4 -6
- data/spec/integration/lhm_spec.rb +3 -4
- data/spec/integration/proxysql_spec.rb +1 -1
- data/spec/integration/sql_retry/lock_wait_spec.rb +2 -2
- data/spec/integration/sql_retry/retry_with_proxysql_spec.rb +8 -7
- data/spec/test_helper.rb +3 -0
- data/spec/unit/chunker_spec.rb +44 -43
- data/spec/unit/connection_spec.rb +37 -12
- data/spec/unit/entangler_spec.rb +31 -9
- data/spec/unit/lhm_spec.rb +17 -0
- data/spec/unit/throttler/slave_lag_spec.rb +1 -1
- metadata +2 -2
data/lib/lhm/sql_retry.rb
CHANGED
@@ -18,6 +18,12 @@ module Lhm
|
|
18
18
|
class SqlRetry
|
19
19
|
RECONNECT_SUCCESSFUL_MESSAGE = "LHM successfully reconnected to initial host:"
|
20
20
|
CLOUDSQL_VERSION_COMMENT = "(Google)"
|
21
|
+
# Will retry for 120 seconds (approximately, since connecting takes time).
|
22
|
+
RECONNECT_RETRY_MAX_ITERATION = 120
|
23
|
+
RECONNECT_RETRY_INTERVAL = 1
|
24
|
+
# Will abort the LHM if it had to reconnect more than 25 times in a single run (indicator that there might be
|
25
|
+
# something wrong with the network and would be better to run the LHM at a later time).
|
26
|
+
RECONNECTION_MAXIMUM = 25
|
21
27
|
|
22
28
|
MYSQL_VAR_NAMES = {
|
23
29
|
hostname: "@@global.hostname",
|
@@ -25,30 +31,24 @@ module Lhm
|
|
25
31
|
version_comment: "@@version_comment",
|
26
32
|
}
|
27
33
|
|
28
|
-
|
29
|
-
class ReconnectToHostSuccessful < Lhm::Error; end
|
30
|
-
|
31
|
-
def initialize(connection, options: {}, reconnect_with_consistent_host: true)
|
34
|
+
def initialize(connection, retry_options: {}, reconnect_with_consistent_host: false)
|
32
35
|
@connection = connection
|
33
|
-
|
34
|
-
|
35
|
-
if (@reconnect_with_consistent_host = reconnect_with_consistent_host)
|
36
|
-
@initial_hostname = hostname
|
37
|
-
@initial_server_id = server_id
|
38
|
-
end
|
36
|
+
self.retry_config = retry_options
|
37
|
+
self.reconnect_with_consistent_host = reconnect_with_consistent_host
|
39
38
|
end
|
40
39
|
|
41
40
|
# Complete explanation of algorithm: https://github.com/Shopify/lhm/pull/112
|
42
|
-
def with_retries(
|
43
|
-
@log_prefix =
|
41
|
+
def with_retries(log_prefix: nil)
|
42
|
+
@log_prefix = log_prefix || "" # No prefix. Just logs
|
44
43
|
|
45
|
-
|
44
|
+
# Amount of time LHM had to reconnect. Aborting if more than RECONNECTION_MAXIMUM
|
45
|
+
reconnection_counter = 0
|
46
46
|
|
47
|
-
Retriable.retriable(retry_config) do
|
47
|
+
Retriable.retriable(@retry_config) do
|
48
48
|
# Using begin -> rescue -> end for Ruby 2.4 compatibility
|
49
49
|
begin
|
50
50
|
if @reconnect_with_consistent_host
|
51
|
-
raise Lhm::Error.new("
|
51
|
+
raise Lhm::Error.new("MySQL host has changed since the start of the LHM. Aborting to avoid data-loss") unless same_host_as_initial?
|
52
52
|
end
|
53
53
|
|
54
54
|
yield(@connection)
|
@@ -57,13 +57,36 @@ module Lhm
|
|
57
57
|
# The error will be raised the connection is still active (i.e. no need to reconnect) or if the connection is
|
58
58
|
# dead (i.e. not active) and @reconnect_with_host is false (i.e. instructed not to reconnect)
|
59
59
|
raise e if @connection.active? || (!@connection.active? && !@reconnect_with_consistent_host)
|
60
|
-
|
60
|
+
|
61
|
+
# Lhm could be stuck in a weird state where it loses connection, reconnects and re looses-connection instantly
|
62
|
+
# after, creating an infinite loop (because of the usage of `retry`). Hence, abort after 25 reconnections
|
63
|
+
raise Lhm::Error.new("LHM reached host reconnection max of #{RECONNECTION_MAXIMUM} times. " \
|
64
|
+
"Please try again later.") if reconnection_counter > RECONNECTION_MAXIMUM
|
65
|
+
|
66
|
+
reconnection_counter += 1
|
67
|
+
if reconnect_with_host_check!
|
68
|
+
retry
|
69
|
+
else
|
70
|
+
raise Lhm::Error.new("LHM tried the reconnection procedure but failed. Aborting")
|
71
|
+
end
|
61
72
|
end
|
62
73
|
end
|
63
74
|
end
|
64
75
|
|
65
|
-
|
66
|
-
|
76
|
+
# Both attributes will have defined setters
|
77
|
+
attr_reader :retry_config, :reconnect_with_consistent_host
|
78
|
+
attr_accessor :connection
|
79
|
+
|
80
|
+
def retry_config=(retry_options)
|
81
|
+
@retry_config = default_retry_config.dup.merge!(retry_options)
|
82
|
+
end
|
83
|
+
|
84
|
+
def reconnect_with_consistent_host=(reconnect)
|
85
|
+
if (@reconnect_with_consistent_host = reconnect)
|
86
|
+
@initial_hostname = hostname
|
87
|
+
@initial_server_id = server_id
|
88
|
+
end
|
89
|
+
end
|
67
90
|
|
68
91
|
private
|
69
92
|
|
@@ -99,34 +122,32 @@ module Lhm
|
|
99
122
|
|
100
123
|
def reconnect_with_host_check!
|
101
124
|
log_with_prefix("Lost connection to MySQL, will retry to connect to same host")
|
102
|
-
|
103
|
-
|
125
|
+
|
126
|
+
RECONNECT_RETRY_MAX_ITERATION.times do
|
127
|
+
begin
|
128
|
+
sleep(RECONNECT_RETRY_INTERVAL)
|
129
|
+
|
104
130
|
# tries to reconnect. On failure will trigger a retry
|
105
131
|
@connection.reconnect!
|
106
132
|
|
107
133
|
if same_host_as_initial?
|
108
134
|
# This is not an actual error, but controlled way to get the parent `Retriable.retriable` to retry
|
109
135
|
# the statement that failed (since the Retriable gem only retries on errors).
|
110
|
-
|
136
|
+
log_with_prefix("LHM successfully reconnected to initial host: #{@initial_hostname} (server_id: #{@initial_server_id})")
|
137
|
+
return true
|
111
138
|
else
|
112
139
|
# New Master --> abort LHM (reconnecting will not change anything)
|
113
|
-
|
140
|
+
log_with_prefix("Reconnected to wrong host. Started migration on: #{@initial_hostname} (server_id: #{@initial_server_id}), but reconnected to: #{hostname} (server_id: #{server_id}).", :error)
|
141
|
+
return false
|
114
142
|
end
|
143
|
+
rescue StandardError => e
|
144
|
+
# Retry if ActiveRecord cannot reach host
|
145
|
+
next if /Lost connection to MySQL server at 'reading initial communication packet'/ === e.message
|
146
|
+
log_with_prefix("Encountered error: [#{e.class}] #{e.message}. Will stop reconnection procedure.", :info)
|
147
|
+
return false
|
115
148
|
end
|
116
|
-
rescue StandardError => e
|
117
|
-
# The parent Retriable.retriable is configured to retry if it encounters an error with the success message.
|
118
|
-
# Therefore, if the connection is re-established successfully AND the host is the same, LHM can retry the query
|
119
|
-
# that originally failed.
|
120
|
-
raise e if reconnect_successful?(e)
|
121
|
-
# If the connection was not successful, the parent retriable will raise any error that originated from the
|
122
|
-
# `@connection.reconnect!`
|
123
|
-
# Therefore, this error will cause the LHM to abort
|
124
|
-
raise Lhm::Error.new("LHM tried the reconnection procedure but failed. Latest error: #{e.message}")
|
125
149
|
end
|
126
|
-
|
127
|
-
|
128
|
-
def reconnect_successful?(e)
|
129
|
-
e.is_a?(ReconnectToHostSuccessful)
|
150
|
+
false
|
130
151
|
end
|
131
152
|
|
132
153
|
# For a full list of configuration options see https://github.com/kamui/retriable
|
@@ -143,9 +164,6 @@ module Lhm
|
|
143
164
|
/Unknown MySQL server host/,
|
144
165
|
/connection is locked to hostgroup/,
|
145
166
|
/The MySQL server is running with the --read-only option so it cannot execute this statement/,
|
146
|
-
],
|
147
|
-
ReconnectToHostSuccessful => [
|
148
|
-
/#{RECONNECT_SUCCESSFUL_MESSAGE}/
|
149
167
|
]
|
150
168
|
},
|
151
169
|
multiplier: 1, # each successive interval grows by this factor
|
@@ -153,28 +171,6 @@ module Lhm
|
|
153
171
|
tries: 20, # Number of attempts to make at running your code block (includes initial attempt).
|
154
172
|
rand_factor: 0, # percentage to randomize the next retry interval time
|
155
173
|
max_elapsed_time: Float::INFINITY, # max total time in seconds that code is allowed to keep being retried
|
156
|
-
on_retry: Proc.new do |exception, try_number, total_elapsed_time, next_interval|
|
157
|
-
if reconnect_successful?(exception)
|
158
|
-
log_with_prefix("#{exception.message} -- triggering retry", :info)
|
159
|
-
else
|
160
|
-
log_with_prefix("#{exception.class}: '#{exception.message}' - #{try_number} tries in #{total_elapsed_time} seconds and #{next_interval} seconds until the next try.", :error)
|
161
|
-
end
|
162
|
-
end
|
163
|
-
}.freeze
|
164
|
-
end
|
165
|
-
|
166
|
-
def host_retry_config
|
167
|
-
{
|
168
|
-
on: {
|
169
|
-
StandardError => [
|
170
|
-
/Lost connection to MySQL server at 'reading initial communication packet'/
|
171
|
-
]
|
172
|
-
},
|
173
|
-
multiplier: 1, # each successive interval grows by this factor
|
174
|
-
base_interval: 0.25, # the initial interval in seconds between tries.
|
175
|
-
tries: 20, # Number of attempts to make at running your code block (includes initial attempt).
|
176
|
-
rand_factor: 0, # percentage to randomize the next retry interval time
|
177
|
-
max_elapsed_time: Float::INFINITY, # max total time in seconds that code is allowed to keep being retried
|
178
174
|
on_retry: Proc.new do |exception, try_number, total_elapsed_time, next_interval|
|
179
175
|
log_with_prefix("#{exception.class}: '#{exception.message}' - #{try_number} tries in #{total_elapsed_time} seconds and #{next_interval} seconds until the next try.", :error)
|
180
176
|
end
|
data/lib/lhm/version.rb
CHANGED
data/lib/lhm.rb
CHANGED
@@ -28,7 +28,7 @@ module Lhm
|
|
28
28
|
extend Throttler
|
29
29
|
extend self
|
30
30
|
|
31
|
-
DEFAULT_LOGGER_OPTIONS =
|
31
|
+
DEFAULT_LOGGER_OPTIONS = { level: Logger::INFO, file: STDOUT }
|
32
32
|
|
33
33
|
# Alters a table with the changes described in the block
|
34
34
|
#
|
@@ -46,15 +46,19 @@ module Lhm
|
|
46
46
|
# Use atomic switch to rename tables (defaults to: true)
|
47
47
|
# If using a version of mysql affected by atomic switch bug, LHM forces user
|
48
48
|
# to set this option (see SqlHelper#supports_atomic_switch?)
|
49
|
+
# @option options [Boolean] :reconnect_with_consistent_host
|
50
|
+
# Active / Deactivate ProxySQL-aware reconnection procedure (default to: false)
|
49
51
|
# @yield [Migrator] Yielded Migrator object records the changes
|
50
52
|
# @return [Boolean] Returns true if the migration finishes
|
51
53
|
# @raise [Error] Raises Lhm::Error in case of a error and aborts the migration
|
52
54
|
def change_table(table_name, options = {}, &block)
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
55
|
+
with_flags(options) do
|
56
|
+
origin = Table.parse(table_name, connection)
|
57
|
+
invoker = Invoker.new(origin, connection)
|
58
|
+
block.call(invoker.migrator)
|
59
|
+
invoker.run(options)
|
60
|
+
true
|
61
|
+
end
|
58
62
|
end
|
59
63
|
|
60
64
|
# Cleanup tables and triggers
|
@@ -86,26 +90,16 @@ module Lhm
|
|
86
90
|
# Setups DB connection
|
87
91
|
#
|
88
92
|
# @param [ActiveRecord::Base] connection ActiveRecord Connection
|
89
|
-
|
90
|
-
|
91
|
-
# Active / Deactivate ProxySQL-aware reconnection procedure (default to: false)
|
92
|
-
def setup(connection, connection_options = {})
|
93
|
-
@@connection = Connection.new(connection: connection, options: connection_options)
|
93
|
+
def setup(connection)
|
94
|
+
@@connection = Connection.new(connection: connection)
|
94
95
|
end
|
95
96
|
|
96
|
-
#
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
if @@connection.nil?
|
103
|
-
raise 'Please call Lhm.setup' unless defined?(ActiveRecord)
|
104
|
-
@@connection = Connection.new(connection: ActiveRecord::Base.connection, options: connection_options || {})
|
105
|
-
else
|
106
|
-
@@connection.options = connection_options unless connection_options.nil?
|
107
|
-
end
|
108
|
-
@@connection
|
97
|
+
# Returns DB connection (or initializes it if not created yet)
|
98
|
+
def connection
|
99
|
+
@@connection ||= begin
|
100
|
+
raise 'Please call Lhm.setup' unless defined?(ActiveRecord)
|
101
|
+
@@connection = Connection.new(connection: ActiveRecord::Base.connection)
|
102
|
+
end
|
109
103
|
end
|
110
104
|
|
111
105
|
def self.logger=(new_logger)
|
@@ -147,4 +141,16 @@ module Lhm
|
|
147
141
|
false
|
148
142
|
end
|
149
143
|
end
|
144
|
+
|
145
|
+
def with_flags(options)
|
146
|
+
old_flags = {
|
147
|
+
reconnect_with_consistent_host: Lhm.connection.reconnect_with_consistent_host,
|
148
|
+
}
|
149
|
+
|
150
|
+
Lhm.connection.reconnect_with_consistent_host = options[:reconnect_with_consistent_host] || false
|
151
|
+
|
152
|
+
yield
|
153
|
+
ensure
|
154
|
+
Lhm.connection.reconnect_with_consistent_host = old_flags[:reconnect_with_consistent_host]
|
155
|
+
end
|
150
156
|
end
|
@@ -39,10 +39,15 @@ describe Lhm::AtomicSwitcher do
|
|
39
39
|
.then
|
40
40
|
.returns([["dummy"]]) # Matches initial host -> triggers retry
|
41
41
|
|
42
|
-
connection = Lhm::Connection.new(connection: ar_connection, options: {
|
42
|
+
connection = Lhm::Connection.new(connection: ar_connection, options: {
|
43
|
+
reconnect_with_consistent_host: true,
|
44
|
+
retriable: {
|
45
|
+
tries: 3,
|
46
|
+
base_interval: 0
|
47
|
+
}
|
48
|
+
})
|
43
49
|
|
44
|
-
|
45
|
-
switcher = Lhm::AtomicSwitcher.new(@migration, connection, retriable: { tries: 3, base_interval: 0 })
|
50
|
+
switcher = Lhm::AtomicSwitcher.new(@migration, connection)
|
46
51
|
|
47
52
|
assert switcher.run
|
48
53
|
|
@@ -58,20 +63,26 @@ describe Lhm::AtomicSwitcher do
|
|
58
63
|
ar_connection.stubs(:data_source_exists?).returns(true)
|
59
64
|
ar_connection.stubs(:active?).returns(true)
|
60
65
|
ar_connection.stubs(:execute).returns([["dummy"]], [["dummy"]], [["dummy"]])
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
connection = Lhm::Connection.new(connection: ar_connection, options: {
|
73
|
-
|
74
|
-
|
66
|
+
.then
|
67
|
+
.raises(ActiveRecord::StatementInvalid, 'Lock wait timeout exceeded; try restarting transaction.')
|
68
|
+
.then
|
69
|
+
.returns([["dummy"]]) # triggers retry 1
|
70
|
+
.then
|
71
|
+
.raises(ActiveRecord::StatementInvalid, 'Lock wait timeout exceeded; try restarting transaction.')
|
72
|
+
.then
|
73
|
+
.returns([["dummy"]]) # triggers retry 2
|
74
|
+
.then
|
75
|
+
.raises(ActiveRecord::StatementInvalid, 'Lock wait timeout exceeded; try restarting transaction.') # triggers retry 2
|
76
|
+
|
77
|
+
connection = Lhm::Connection.new(connection: ar_connection, options: {
|
78
|
+
reconnect_with_consistent_host: true,
|
79
|
+
retriable: {
|
80
|
+
tries: 2,
|
81
|
+
base_interval: 0
|
82
|
+
}
|
83
|
+
})
|
84
|
+
|
85
|
+
switcher = Lhm::AtomicSwitcher.new(@migration, connection)
|
75
86
|
|
76
87
|
assert_raises(ActiveRecord::StatementInvalid) { switcher.run }
|
77
88
|
end
|
@@ -173,6 +173,9 @@ describe Lhm::Chunker do
|
|
173
173
|
printer.expect(:notify, :return_value, [Integer, Integer])
|
174
174
|
printer.expect(:end, :return_value, [])
|
175
175
|
|
176
|
+
Lhm::Throttler::Slave.any_instance.stubs(:slave_hosts).returns(['127.0.0.1'])
|
177
|
+
Lhm::Throttler::SlaveLag.any_instance.stubs(:master_slave_hosts).returns(['127.0.0.1'])
|
178
|
+
|
176
179
|
Lhm::Chunker.new(
|
177
180
|
@migration, connection, { throttler: Lhm::Throttler::SlaveLag.new(stride: 100), printer: printer }
|
178
181
|
).run
|
@@ -215,17 +218,16 @@ describe Lhm::Chunker do
|
|
215
218
|
printer.expects(:verify)
|
216
219
|
printer.expects(:end)
|
217
220
|
|
218
|
-
|
221
|
+
Lhm::Throttler::Slave.any_instance.stubs(:slave_hosts).returns(['127.0.0.1'])
|
222
|
+
Lhm::Throttler::SlaveLag.any_instance.stubs(:master_slave_hosts).returns(['127.0.0.1'])
|
219
223
|
|
220
|
-
|
221
|
-
['127.0.0.1']
|
222
|
-
end
|
224
|
+
throttler = Lhm::Throttler::SlaveLag.new(stride: 10, allowed_lag: 0)
|
223
225
|
|
224
226
|
if master_slave_mode?
|
225
227
|
def throttler.slave_connection(slave)
|
226
228
|
config = ActiveRecord::Base.connection_pool.db_config.configuration_hash.dup
|
227
229
|
config[:host] = slave
|
228
|
-
config[:port] =
|
230
|
+
config[:port] = 33007
|
229
231
|
ActiveRecord::Base.send('mysql2_connection', config)
|
230
232
|
end
|
231
233
|
end
|
@@ -62,18 +62,16 @@ module IntegrationHelper
|
|
62
62
|
)
|
63
63
|
end
|
64
64
|
|
65
|
-
def connect_master_with_toxiproxy!
|
65
|
+
def connect_master_with_toxiproxy!
|
66
66
|
connect!(
|
67
67
|
'127.0.0.1',
|
68
68
|
$db_config['master_toxic']['port'],
|
69
69
|
$db_config['master_toxic']['user'],
|
70
|
-
$db_config['master_toxic']['password']
|
71
|
-
with_retry)
|
70
|
+
$db_config['master_toxic']['password'])
|
72
71
|
end
|
73
72
|
|
74
|
-
def connect!(hostname, port, user, password
|
75
|
-
|
76
|
-
Lhm.setup(adapter,{reconnect_with_consistent_host: with_retry})
|
73
|
+
def connect!(hostname, port, user, password)
|
74
|
+
Lhm.setup(ar_conn(hostname, port, user, password))
|
77
75
|
unless defined?(@@cleaned_up)
|
78
76
|
Lhm.cleanup(true)
|
79
77
|
@@cleaned_up = true
|
@@ -592,7 +592,7 @@ describe Lhm do
|
|
592
592
|
end
|
593
593
|
|
594
594
|
it " should not try to reconnect if reconnect_with_consistent_host is not provided" do
|
595
|
-
connect_master_with_toxiproxy!
|
595
|
+
connect_master_with_toxiproxy!
|
596
596
|
|
597
597
|
table_create(:users)
|
598
598
|
100.times { |n| execute("insert into users set reference = '#{ n }'") }
|
@@ -610,7 +610,7 @@ describe Lhm do
|
|
610
610
|
end
|
611
611
|
|
612
612
|
it "should reconnect if reconnect_with_consistent_host is true" do
|
613
|
-
connect_master_with_toxiproxy!
|
613
|
+
connect_master_with_toxiproxy!
|
614
614
|
mysql_disabled = false
|
615
615
|
|
616
616
|
table_create(:users)
|
@@ -635,7 +635,7 @@ describe Lhm do
|
|
635
635
|
method_added(:insert_and_return_count_of_rows_created)
|
636
636
|
end
|
637
637
|
|
638
|
-
Lhm.change_table(:users, :
|
638
|
+
Lhm.change_table(:users, atomic_switch: false, reconnect_with_consistent_host: true) do |t|
|
639
639
|
t.ddl("ALTER TABLE #{t.name} CHANGE id id bigint (20) NOT NULL")
|
640
640
|
t.ddl("ALTER TABLE #{t.name} DROP PRIMARY KEY, ADD PRIMARY KEY (username, id)")
|
641
641
|
t.ddl("ALTER TABLE #{t.name} ADD INDEX (id)")
|
@@ -645,7 +645,6 @@ describe Lhm do
|
|
645
645
|
log_lines = @logs.string.split("\n")
|
646
646
|
|
647
647
|
assert log_lines.one?{ |line| line.include?("Lost connection to MySQL, will retry to connect to same host")}
|
648
|
-
assert log_lines.any?{ |line| line.include?("Lost connection to MySQL server at 'reading initial communication packet'")}
|
649
648
|
assert log_lines.one?{ |line| line.include?("LHM successfully reconnected to initial host")}
|
650
649
|
assert log_lines.one?{ |line| line.include?("100% complete")}
|
651
650
|
|
@@ -29,6 +29,6 @@ describe "ProxySQL integration" do
|
|
29
29
|
port: "33005",
|
30
30
|
)
|
31
31
|
|
32
|
-
assert_equal conn.query("
|
32
|
+
assert_equal conn.query("SELECT @@global.hostname as host #{Lhm::ProxySQLHelper::ANNOTATION}").each.first["host"], "mysql-1"
|
33
33
|
end
|
34
34
|
end
|
@@ -55,7 +55,7 @@ describe Lhm::SqlRetry do
|
|
55
55
|
it "successfully executes the SQL despite the errors encountered" do
|
56
56
|
# Start a thread to retry, once the lock is held, execute the block
|
57
57
|
@helper.with_waiting_lock do |waiting_connection|
|
58
|
-
sql_retry = Lhm::SqlRetry.new(waiting_connection,
|
58
|
+
sql_retry = Lhm::SqlRetry.new(waiting_connection, retry_options: {
|
59
59
|
base_interval: 0.2, # first retry after 200ms
|
60
60
|
multiplier: 1, # subsequent retries wait 1x longer than first retry (no change)
|
61
61
|
tries: 3, # we only need 3 tries (including the first) for the scenario described below
|
@@ -98,7 +98,7 @@ describe Lhm::SqlRetry do
|
|
98
98
|
puts "*" * 64
|
99
99
|
# Start a thread to retry, once the lock is held, execute the block
|
100
100
|
@helper.with_waiting_lock do |waiting_connection|
|
101
|
-
sql_retry = Lhm::SqlRetry.new(waiting_connection,
|
101
|
+
sql_retry = Lhm::SqlRetry.new(waiting_connection, retry_options: {
|
102
102
|
base_interval: 0.2, # first retry after 200ms
|
103
103
|
multiplier: 1, # subsequent retries wait 1x longer than first retry (no change)
|
104
104
|
tries: 2, # we need 3 tries (including the first) for the scenario described below, but we only get two...we will fail
|
@@ -18,7 +18,7 @@ describe Lhm::SqlRetry, "ProxiSQL tests for LHM retry" do
|
|
18
18
|
|
19
19
|
@connection = DBConnectionHelper::new_mysql_connection(:proxysql, true, true)
|
20
20
|
|
21
|
-
@lhm_retry = Lhm::SqlRetry.new(@connection,
|
21
|
+
@lhm_retry = Lhm::SqlRetry.new(@connection, retry_options: {},
|
22
22
|
reconnect_with_consistent_host: true)
|
23
23
|
end
|
24
24
|
|
@@ -38,7 +38,7 @@ describe Lhm::SqlRetry, "ProxiSQL tests for LHM retry" do
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
assert_equal Lhm::Error, e.class
|
41
|
-
assert_match(/LHM tried the reconnection procedure but failed.
|
41
|
+
assert_match(/LHM tried the reconnection procedure but failed. Aborting/, e.message)
|
42
42
|
end
|
43
43
|
|
44
44
|
it "Will retry until connection is achieved" do
|
@@ -61,9 +61,10 @@ describe Lhm::SqlRetry, "ProxiSQL tests for LHM retry" do
|
|
61
61
|
it "Will abort if new writer is not same host" do
|
62
62
|
# The hostname will be constant before the blip
|
63
63
|
Lhm::SqlRetry.any_instance.stubs(:hostname).returns("mysql-1").then.returns("mysql-2")
|
64
|
+
Lhm::SqlRetry.any_instance.stubs(:server_id).returns(1).then.returns(2)
|
64
65
|
|
65
66
|
# Need new instance for stub to take into effect
|
66
|
-
lhm_retry = Lhm::SqlRetry.new(@connection,
|
67
|
+
lhm_retry = Lhm::SqlRetry.new(@connection, retry_options: {},
|
67
68
|
reconnect_with_consistent_host: true)
|
68
69
|
|
69
70
|
e = assert_raises Lhm::Error do
|
@@ -76,12 +77,12 @@ describe Lhm::SqlRetry, "ProxiSQL tests for LHM retry" do
|
|
76
77
|
end
|
77
78
|
|
78
79
|
assert_equal e.class, Lhm::Error
|
79
|
-
assert_match(/LHM tried the reconnection procedure but failed.
|
80
|
+
assert_match(/LHM tried the reconnection procedure but failed. Aborting/, e.message)
|
80
81
|
|
81
82
|
logs = @logger.string.split("\n")
|
82
83
|
|
83
84
|
assert logs.first.include?("Lost connection to MySQL, will retry to connect to same host")
|
84
|
-
assert logs.last.include?("
|
85
|
+
assert logs.last.include?("Reconnected to wrong host. Started migration on: mysql-1 (server_id: 1), but reconnected to: mysql-2 (server_id: 2).")
|
85
86
|
end
|
86
87
|
|
87
88
|
it "Will abort if failover happens (mimicked with proxySQL)" do
|
@@ -98,11 +99,11 @@ describe Lhm::SqlRetry, "ProxiSQL tests for LHM retry" do
|
|
98
99
|
end
|
99
100
|
|
100
101
|
assert_equal e.class, Lhm::Error
|
101
|
-
assert_match(/LHM tried the reconnection procedure but failed.
|
102
|
+
assert_match(/LHM tried the reconnection procedure but failed. Aborting/, e.message)
|
102
103
|
|
103
104
|
logs = @logger.string.split("\n")
|
104
105
|
|
105
106
|
assert logs.first.include?("Lost connection to MySQL, will retry to connect to same host")
|
106
|
-
assert logs.last.include?("
|
107
|
+
assert logs.last.include?("Reconnected to wrong host. Started migration on: mysql-1 (server_id: 1), but reconnected to: mysql-2 (server_id: 2).")
|
107
108
|
end
|
108
109
|
end
|
data/spec/test_helper.rb
CHANGED
@@ -28,6 +28,9 @@ logger = Logger.new STDOUT
|
|
28
28
|
logger.level = Logger::WARN
|
29
29
|
Lhm.logger = logger
|
30
30
|
|
31
|
+
# Want test to be efficient without having to wait the normal value of 120s
|
32
|
+
Lhm::SqlRetry::RECONNECT_RETRY_MAX_ITERATION = 4
|
33
|
+
|
31
34
|
def without_verbose(&block)
|
32
35
|
old_verbose, $VERBOSE = $VERBOSE, nil
|
33
36
|
yield
|