lhm-shopify 3.5.4 → 4.0.0
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/.github/workflows/test.yml +6 -6
- data/Appraisals +8 -13
- data/CHANGELOG.md +13 -0
- data/Gemfile.lock +22 -20
- data/README.md +14 -7
- data/dev.yml +12 -8
- data/docker-compose.yml +2 -0
- data/gemfiles/activerecord_6.0.gemfile +1 -1
- data/gemfiles/activerecord_6.0.gemfile.lock +25 -21
- data/gemfiles/activerecord_6.1.gemfile.lock +17 -13
- data/gemfiles/{activerecord_7.0.0.alpha2.gemfile → activerecord_7.0.gemfile} +1 -1
- data/gemfiles/{activerecord_7.0.0.alpha2.gemfile.lock → activerecord_7.0.gemfile.lock} +23 -19
- data/gemfiles/{activerecord_5.2.gemfile → activerecord_7.1.0.beta1.gemfile} +1 -3
- data/gemfiles/activerecord_7.1.0.beta1.gemfile.lock +81 -0
- data/lhm.gemspec +1 -1
- data/lib/lhm/sql_helper.rb +1 -1
- data/lib/lhm/sql_retry.rb +37 -47
- data/lib/lhm/throttler/replica_lag.rb +162 -0
- data/lib/lhm/throttler/slave_lag.rb +5 -155
- data/lib/lhm/throttler/threads_running.rb +3 -1
- data/lib/lhm/throttler.rb +7 -3
- data/lib/lhm/version.rb +1 -1
- data/spec/.lhm.example +1 -1
- data/spec/README.md +8 -9
- data/spec/integration/atomic_switcher_spec.rb +2 -2
- data/spec/integration/chunk_insert_spec.rb +2 -2
- data/spec/integration/chunker_spec.rb +33 -38
- data/spec/integration/database.yml +1 -1
- data/spec/integration/entangler_spec.rb +4 -4
- data/spec/integration/integration_helper.rb +12 -12
- data/spec/integration/lhm_spec.rb +41 -32
- data/spec/integration/locked_switcher_spec.rb +2 -2
- data/spec/integration/sql_retry/retry_with_proxysql_spec.rb +6 -5
- data/spec/integration/toxiproxy_helper.rb +1 -1
- data/spec/test_helper.rb +3 -0
- data/spec/unit/printer_spec.rb +2 -6
- data/spec/unit/sql_helper_spec.rb +2 -2
- data/spec/unit/throttler/{slave_lag_spec.rb → replica_lag_spec.rb} +79 -79
- data/spec/unit/throttler/threads_running_spec.rb +18 -0
- data/spec/unit/throttler_spec.rb +8 -8
- metadata +10 -9
- data/gemfiles/activerecord_5.2.gemfile.lock +0 -65
@@ -28,7 +28,7 @@ describe Lhm::Chunker do
|
|
28
28
|
|
29
29
|
Lhm::Chunker.new(@migration, connection, {throttler: throttler, printer: printer} ).run
|
30
30
|
|
31
|
-
|
31
|
+
replica do
|
32
32
|
value(count_all(@destination.name)).must_equal(1)
|
33
33
|
end
|
34
34
|
|
@@ -41,7 +41,7 @@ describe Lhm::Chunker do
|
|
41
41
|
|
42
42
|
Lhm::Chunker.new(@migration, connection, {throttler: throttler, printer: printer} ).run
|
43
43
|
|
44
|
-
|
44
|
+
replica do
|
45
45
|
value(count_all(@destination.name)).must_equal(2)
|
46
46
|
end
|
47
47
|
end
|
@@ -57,7 +57,7 @@ describe Lhm::Chunker do
|
|
57
57
|
|
58
58
|
Lhm::Chunker.new(migration, connection, {throttler: throttler, printer: printer} ).run
|
59
59
|
|
60
|
-
|
60
|
+
replica do
|
61
61
|
value(count_all(destination.name)).must_equal(2)
|
62
62
|
end
|
63
63
|
end
|
@@ -127,7 +127,7 @@ describe Lhm::Chunker do
|
|
127
127
|
|
128
128
|
Lhm::Chunker.new(@migration, connection, {throttler: throttler, printer: printer} ).run
|
129
129
|
|
130
|
-
|
130
|
+
replica do
|
131
131
|
value(count_all(@destination.name)).must_equal(0)
|
132
132
|
end
|
133
133
|
|
@@ -136,20 +136,17 @@ describe Lhm::Chunker do
|
|
136
136
|
it 'should copy 23 rows from origin to destination in one shot, regardless of the value of the id' do
|
137
137
|
23.times { |n| execute("insert into origin set id = '#{ n * n + 23 }'") }
|
138
138
|
|
139
|
-
printer =
|
140
|
-
printer.
|
141
|
-
printer.
|
139
|
+
printer = mock("printer")
|
140
|
+
printer.expects(:notify).with(kind_of(Integer), kind_of(Integer))
|
141
|
+
printer.expects(:end)
|
142
142
|
|
143
143
|
Lhm::Chunker.new(
|
144
144
|
@migration, connection, { throttler: throttler, printer: printer }
|
145
145
|
).run
|
146
146
|
|
147
|
-
|
147
|
+
replica do
|
148
148
|
value(count_all(@destination.name)).must_equal(23)
|
149
149
|
end
|
150
|
-
|
151
|
-
printer.verify
|
152
|
-
|
153
150
|
end
|
154
151
|
|
155
152
|
it 'should copy all the records of a table, even if the last chunk starts with the last record of it.' do
|
@@ -160,42 +157,40 @@ describe Lhm::Chunker do
|
|
160
157
|
@migration, connection, { throttler: Lhm::Throttler::Time.new(stride: 10), printer: printer }
|
161
158
|
).run
|
162
159
|
|
163
|
-
|
160
|
+
replica do
|
164
161
|
value(count_all(@destination.name)).must_equal(11)
|
165
162
|
end
|
166
163
|
|
167
164
|
end
|
168
165
|
|
169
|
-
it 'should copy 23 rows from origin to destination in one shot with
|
166
|
+
it 'should copy 23 rows from origin to destination in one shot with replica lag based throttler, regardless of the value of the id' do
|
170
167
|
23.times { |n| execute("insert into origin set id = '#{ 100000 + n * n + 23 }'") }
|
171
168
|
|
172
|
-
printer =
|
173
|
-
printer.
|
174
|
-
printer.
|
169
|
+
printer = mock("printer")
|
170
|
+
printer.expects(:notify).with(kind_of(Integer), kind_of(Integer))
|
171
|
+
printer.expects(:end)
|
175
172
|
|
176
|
-
Lhm::Throttler::
|
177
|
-
Lhm::Throttler::
|
173
|
+
Lhm::Throttler::Replica.any_instance.stubs(:replica_hosts).returns(['127.0.0.1'])
|
174
|
+
Lhm::Throttler::ReplicaLag.any_instance.stubs(:master_replica_hosts).returns(['127.0.0.1'])
|
178
175
|
|
179
176
|
Lhm::Chunker.new(
|
180
|
-
@migration, connection, { throttler: Lhm::Throttler::
|
177
|
+
@migration, connection, { throttler: Lhm::Throttler::ReplicaLag.new(stride: 100), printer: printer }
|
181
178
|
).run
|
182
179
|
|
183
|
-
|
180
|
+
replica do
|
184
181
|
value(count_all(@destination.name)).must_equal(23)
|
185
182
|
end
|
186
|
-
|
187
|
-
printer.verify
|
188
183
|
end
|
189
184
|
|
190
|
-
it 'should throttle work stride based on
|
185
|
+
it 'should throttle work stride based on replica lag' do
|
191
186
|
15.times { |n| execute("insert into origin set id = '#{ (n * n) + 1 }'") }
|
192
187
|
|
193
188
|
printer = mock()
|
194
189
|
printer.expects(:notify).with(instance_of(Integer), instance_of(Integer)).twice
|
195
190
|
printer.expects(:end)
|
196
191
|
|
197
|
-
throttler = Lhm::Throttler::
|
198
|
-
def throttler.
|
192
|
+
throttler = Lhm::Throttler::ReplicaLag.new(stride: 10, allowed_lag: 0)
|
193
|
+
def throttler.max_current_replica_lag
|
199
194
|
1
|
200
195
|
end
|
201
196
|
|
@@ -203,14 +198,14 @@ describe Lhm::Chunker do
|
|
203
198
|
@migration, connection, { throttler: throttler, printer: printer }
|
204
199
|
).run
|
205
200
|
|
206
|
-
assert_equal(Lhm::Throttler::
|
201
|
+
assert_equal(Lhm::Throttler::ReplicaLag::INITIAL_TIMEOUT * 2 * 2, throttler.timeout_seconds)
|
207
202
|
|
208
|
-
|
203
|
+
replica do
|
209
204
|
value(count_all(@destination.name)).must_equal(15)
|
210
205
|
end
|
211
206
|
end
|
212
207
|
|
213
|
-
it 'should detect a single
|
208
|
+
it 'should detect a single replica with no lag in the default configuration' do
|
214
209
|
15.times { |n| execute("insert into origin set id = '#{ (n * n) + 1 }'") }
|
215
210
|
|
216
211
|
printer = mock()
|
@@ -218,15 +213,15 @@ describe Lhm::Chunker do
|
|
218
213
|
printer.expects(:verify)
|
219
214
|
printer.expects(:end)
|
220
215
|
|
221
|
-
Lhm::Throttler::
|
222
|
-
Lhm::Throttler::
|
216
|
+
Lhm::Throttler::Replica.any_instance.stubs(:replica_hosts).returns(['127.0.0.1'])
|
217
|
+
Lhm::Throttler::ReplicaLag.any_instance.stubs(:master_replica_hosts).returns(['127.0.0.1'])
|
223
218
|
|
224
|
-
throttler = Lhm::Throttler::
|
219
|
+
throttler = Lhm::Throttler::ReplicaLag.new(stride: 10, allowed_lag: 0)
|
225
220
|
|
226
|
-
if
|
227
|
-
def throttler.
|
221
|
+
if master_replica_mode?
|
222
|
+
def throttler.replica_connection(replica)
|
228
223
|
config = ActiveRecord::Base.connection_pool.db_config.configuration_hash.dup
|
229
|
-
config[:host] =
|
224
|
+
config[:host] = replica
|
230
225
|
config[:port] = 33007
|
231
226
|
ActiveRecord::Base.send('mysql2_connection', config)
|
232
227
|
end
|
@@ -236,10 +231,10 @@ describe Lhm::Chunker do
|
|
236
231
|
@migration, connection, { throttler: throttler, printer: printer }
|
237
232
|
).run
|
238
233
|
|
239
|
-
assert_equal(Lhm::Throttler::
|
240
|
-
assert_equal(0, throttler.send(:
|
234
|
+
assert_equal(Lhm::Throttler::ReplicaLag::INITIAL_TIMEOUT, throttler.timeout_seconds)
|
235
|
+
assert_equal(0, throttler.send(:max_current_replica_lag))
|
241
236
|
|
242
|
-
|
237
|
+
replica do
|
243
238
|
value(count_all(@destination.name)).must_equal(15)
|
244
239
|
end
|
245
240
|
|
@@ -261,7 +256,7 @@ describe Lhm::Chunker do
|
|
261
256
|
|
262
257
|
assert_match "Verification failed, aborting early", exception.message
|
263
258
|
|
264
|
-
|
259
|
+
replica do
|
265
260
|
value(count_all(@destination.name)).must_equal(0)
|
266
261
|
end
|
267
262
|
end
|
@@ -27,7 +27,7 @@ describe Lhm::Entangler do
|
|
27
27
|
execute("insert into origin (common) values ('inserted')")
|
28
28
|
end
|
29
29
|
|
30
|
-
|
30
|
+
replica do
|
31
31
|
value(count(:destination, 'common', 'inserted')).must_equal(1)
|
32
32
|
end
|
33
33
|
end
|
@@ -39,7 +39,7 @@ describe Lhm::Entangler do
|
|
39
39
|
execute("delete from origin where common = 'inserted'")
|
40
40
|
end
|
41
41
|
|
42
|
-
|
42
|
+
replica do
|
43
43
|
value(count(:destination, 'common', 'inserted')).must_equal(0)
|
44
44
|
end
|
45
45
|
end
|
@@ -50,7 +50,7 @@ describe Lhm::Entangler do
|
|
50
50
|
execute("update origin set common = 'updated'")
|
51
51
|
end
|
52
52
|
|
53
|
-
|
53
|
+
replica do
|
54
54
|
value(count(:destination, 'common', 'updated')).must_equal(1)
|
55
55
|
end
|
56
56
|
end
|
@@ -60,7 +60,7 @@ describe Lhm::Entangler do
|
|
60
60
|
|
61
61
|
execute("insert into origin (common) values ('inserted')")
|
62
62
|
|
63
|
-
|
63
|
+
replica do
|
64
64
|
value(count(:destination, 'common', 'inserted')).must_equal(0)
|
65
65
|
end
|
66
66
|
end
|
@@ -53,12 +53,12 @@ module IntegrationHelper
|
|
53
53
|
)
|
54
54
|
end
|
55
55
|
|
56
|
-
def
|
56
|
+
def connect_replica!
|
57
57
|
connect!(
|
58
58
|
'127.0.0.1',
|
59
|
-
$db_config['
|
60
|
-
$db_config['
|
61
|
-
$db_config['
|
59
|
+
$db_config['replica']['port'],
|
60
|
+
$db_config['replica']['user'],
|
61
|
+
$db_config['replica']['password'],
|
62
62
|
)
|
63
63
|
end
|
64
64
|
|
@@ -113,12 +113,12 @@ module IntegrationHelper
|
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
116
|
-
def
|
117
|
-
if
|
118
|
-
|
116
|
+
def replica(&block)
|
117
|
+
if master_replica_mode?
|
118
|
+
connect_replica!
|
119
119
|
|
120
|
-
# need to wait for the
|
121
|
-
# check the master binlog position and wait for the
|
120
|
+
# need to wait for the replica to catch up. a better method would be to
|
121
|
+
# check the master binlog position and wait for the replica to catch up
|
122
122
|
# to that position.
|
123
123
|
sleep 1
|
124
124
|
else
|
@@ -127,7 +127,7 @@ module IntegrationHelper
|
|
127
127
|
|
128
128
|
yield block
|
129
129
|
|
130
|
-
if
|
130
|
+
if master_replica_mode?
|
131
131
|
connect_master!
|
132
132
|
end
|
133
133
|
end
|
@@ -215,8 +215,8 @@ module IntegrationHelper
|
|
215
215
|
# Environment
|
216
216
|
#
|
217
217
|
|
218
|
-
def
|
219
|
-
!!ENV['
|
218
|
+
def master_replica_mode?
|
219
|
+
!!ENV['MASTER_REPLICA']
|
220
220
|
end
|
221
221
|
|
222
222
|
#
|
@@ -17,7 +17,7 @@ describe Lhm do
|
|
17
17
|
t.add_column(:logins, "int(12) default '0'")
|
18
18
|
end
|
19
19
|
|
20
|
-
|
20
|
+
replica do
|
21
21
|
value(table_read(:users).columns['logins']).must_equal({
|
22
22
|
:type => 'int(12)',
|
23
23
|
:is_nullable => 'YES',
|
@@ -35,7 +35,7 @@ describe Lhm do
|
|
35
35
|
t.add_column(:logins, "int(12) default '0'")
|
36
36
|
end
|
37
37
|
|
38
|
-
|
38
|
+
replica do
|
39
39
|
value(table_read(:custom_primary_key).columns['logins']).must_equal({
|
40
40
|
:type => 'int(12)',
|
41
41
|
:is_nullable => 'YES',
|
@@ -53,7 +53,7 @@ describe Lhm do
|
|
53
53
|
t.add_column(:logins, "int(12) default '0'")
|
54
54
|
end
|
55
55
|
|
56
|
-
|
56
|
+
replica do
|
57
57
|
value(table_read(:composite_primary_key).columns['logins']).must_equal({
|
58
58
|
:type => 'int(12)',
|
59
59
|
:is_nullable => 'YES',
|
@@ -82,7 +82,7 @@ describe Lhm do
|
|
82
82
|
t.ddl("ALTER TABLE #{t.name} CHANGE id id bigint (20) NOT NULL AUTO_INCREMENT")
|
83
83
|
end
|
84
84
|
|
85
|
-
|
85
|
+
replica do
|
86
86
|
value(connection.primary_key('users')).must_equal(['username', 'id'])
|
87
87
|
end
|
88
88
|
end
|
@@ -104,7 +104,7 @@ describe Lhm do
|
|
104
104
|
describe 'when no additional data is inserted into the table' do
|
105
105
|
|
106
106
|
it 'migrates the existing data' do
|
107
|
-
|
107
|
+
replica do
|
108
108
|
value(count_all(:permissions)).must_equal(11)
|
109
109
|
end
|
110
110
|
end
|
@@ -120,7 +120,7 @@ describe Lhm do
|
|
120
120
|
end
|
121
121
|
|
122
122
|
it 'migrates all data' do
|
123
|
-
|
123
|
+
replica do
|
124
124
|
value(count_all(:permissions)).must_equal(13)
|
125
125
|
end
|
126
126
|
end
|
@@ -132,7 +132,7 @@ describe Lhm do
|
|
132
132
|
t.add_column(:logins, "INT(12) DEFAULT '0'")
|
133
133
|
end
|
134
134
|
|
135
|
-
|
135
|
+
replica do
|
136
136
|
value(table_read(:users).columns['logins']).must_equal({
|
137
137
|
:type => 'int(12)',
|
138
138
|
:is_nullable => 'YES',
|
@@ -150,7 +150,7 @@ describe Lhm do
|
|
150
150
|
t.add_column(:logins, "INT(12) DEFAULT '0'")
|
151
151
|
end
|
152
152
|
|
153
|
-
|
153
|
+
replica do
|
154
154
|
value(count_all(:users)).must_equal(23)
|
155
155
|
end
|
156
156
|
end
|
@@ -160,7 +160,7 @@ describe Lhm do
|
|
160
160
|
t.remove_column(:comment)
|
161
161
|
end
|
162
162
|
|
163
|
-
|
163
|
+
replica do
|
164
164
|
assert_nil table_read(:users).columns['comment']
|
165
165
|
end
|
166
166
|
end
|
@@ -170,7 +170,7 @@ describe Lhm do
|
|
170
170
|
t.add_index([:comment, :created_at])
|
171
171
|
end
|
172
172
|
|
173
|
-
|
173
|
+
replica do
|
174
174
|
value(index_on_columns?(:users, [:comment, :created_at])).must_equal(true)
|
175
175
|
end
|
176
176
|
end
|
@@ -180,7 +180,7 @@ describe Lhm do
|
|
180
180
|
t.add_index([:comment, :created_at], :my_index_name)
|
181
181
|
end
|
182
182
|
|
183
|
-
|
183
|
+
replica do
|
184
184
|
value(index?(:users, :my_index_name)).must_equal(true)
|
185
185
|
end
|
186
186
|
end
|
@@ -190,7 +190,7 @@ describe Lhm do
|
|
190
190
|
t.add_index(:group)
|
191
191
|
end
|
192
192
|
|
193
|
-
|
193
|
+
replica do
|
194
194
|
value(index_on_columns?(:users, :group)).must_equal(true)
|
195
195
|
end
|
196
196
|
end
|
@@ -200,7 +200,7 @@ describe Lhm do
|
|
200
200
|
t.add_unique_index(:comment)
|
201
201
|
end
|
202
202
|
|
203
|
-
|
203
|
+
replica do
|
204
204
|
value(index_on_columns?(:users, :comment, :unique)).must_equal(true)
|
205
205
|
end
|
206
206
|
end
|
@@ -210,7 +210,7 @@ describe Lhm do
|
|
210
210
|
t.remove_index([:username, :created_at])
|
211
211
|
end
|
212
212
|
|
213
|
-
|
213
|
+
replica do
|
214
214
|
value(index_on_columns?(:users, [:username, :created_at])).must_equal(false)
|
215
215
|
end
|
216
216
|
end
|
@@ -220,7 +220,7 @@ describe Lhm do
|
|
220
220
|
t.remove_index([:username, :group])
|
221
221
|
end
|
222
222
|
|
223
|
-
|
223
|
+
replica do
|
224
224
|
value(index?(:users, :index_with_a_custom_name)).must_equal(false)
|
225
225
|
end
|
226
226
|
end
|
@@ -230,17 +230,27 @@ describe Lhm do
|
|
230
230
|
t.remove_index(:irrelevant_column_name, :index_with_a_custom_name)
|
231
231
|
end
|
232
232
|
|
233
|
-
|
233
|
+
replica do
|
234
234
|
value(index?(:users, :index_with_a_custom_name)).must_equal(false)
|
235
235
|
end
|
236
236
|
end
|
237
237
|
|
238
|
+
it 'should add an index with column sizes' do
|
239
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
240
|
+
t.add_index(["username(6)", "group (10)", "comment (10)"])
|
241
|
+
end
|
242
|
+
|
243
|
+
replica do
|
244
|
+
value(index_on_columns?(:users, [:username, :group, :comment])).must_equal(true)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
238
248
|
it 'should apply a ddl statement' do
|
239
249
|
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
240
250
|
t.ddl('alter table %s add column flag tinyint(1)' % t.name)
|
241
251
|
end
|
242
252
|
|
243
|
-
|
253
|
+
replica do
|
244
254
|
value(table_read(:users).columns['flag']).must_equal({
|
245
255
|
:type => 'tinyint(1)',
|
246
256
|
:is_nullable => 'YES',
|
@@ -256,7 +266,7 @@ describe Lhm do
|
|
256
266
|
t.change_column(:comment, "varchar(20) DEFAULT 'none' NOT NULL")
|
257
267
|
end
|
258
268
|
|
259
|
-
|
269
|
+
replica do
|
260
270
|
value(table_read(:users).columns['comment']).must_equal({
|
261
271
|
:type => 'varchar(20)',
|
262
272
|
:is_nullable => 'NO',
|
@@ -274,7 +284,7 @@ describe Lhm do
|
|
274
284
|
t.change_column(:id, 'int(5)')
|
275
285
|
end
|
276
286
|
|
277
|
-
|
287
|
+
replica do
|
278
288
|
value(table_read(:small_table).columns['id']).must_equal({
|
279
289
|
:type => 'int(5)',
|
280
290
|
:is_nullable => 'NO',
|
@@ -293,7 +303,7 @@ describe Lhm do
|
|
293
303
|
t.rename_column(:username, :login)
|
294
304
|
end
|
295
305
|
|
296
|
-
|
306
|
+
replica do
|
297
307
|
table_data = table_read(:users)
|
298
308
|
assert_nil table_data.columns['username']
|
299
309
|
value(table_read(:users).columns['login']).must_equal({
|
@@ -318,7 +328,7 @@ describe Lhm do
|
|
318
328
|
t.rename_column(:group, :fnord)
|
319
329
|
end
|
320
330
|
|
321
|
-
|
331
|
+
replica do
|
322
332
|
table_data = table_read(:users)
|
323
333
|
assert_nil table_data.columns['group']
|
324
334
|
value(table_read(:users).columns['fnord']).must_equal({
|
@@ -345,7 +355,7 @@ describe Lhm do
|
|
345
355
|
t.rename_column(:username, :user_name)
|
346
356
|
end
|
347
357
|
|
348
|
-
|
358
|
+
replica do
|
349
359
|
table_data = table_read(:users)
|
350
360
|
assert_nil table_data.columns['username']
|
351
361
|
value(table_read(:users).columns['user_name']).must_equal({
|
@@ -373,7 +383,7 @@ describe Lhm do
|
|
373
383
|
t.rename_column(:reference, :ref)
|
374
384
|
end
|
375
385
|
|
376
|
-
|
386
|
+
replica do
|
377
387
|
table_data = table_read(:users)
|
378
388
|
assert_nil table_data.columns['reference']
|
379
389
|
value(table_read(:users).columns['ref']).must_equal({
|
@@ -400,7 +410,7 @@ describe Lhm do
|
|
400
410
|
t.rename_column(:group, :fnord)
|
401
411
|
end
|
402
412
|
|
403
|
-
|
413
|
+
replica do
|
404
414
|
table_data = table_read(:users)
|
405
415
|
assert_nil table_data.columns['group']
|
406
416
|
value(table_read(:users).columns['fnord']).must_equal({
|
@@ -425,7 +435,7 @@ describe Lhm do
|
|
425
435
|
t.rename_column(:username, :user_name)
|
426
436
|
end
|
427
437
|
|
428
|
-
|
438
|
+
replica do
|
429
439
|
table_data = table_read(:users)
|
430
440
|
assert_nil table_data.columns['username']
|
431
441
|
value(table_read(:users).columns['user_name']).must_equal({
|
@@ -452,7 +462,7 @@ describe Lhm do
|
|
452
462
|
t.rename_column(:username, :user_name)
|
453
463
|
end
|
454
464
|
|
455
|
-
|
465
|
+
replica do
|
456
466
|
table_data = table_read(:users)
|
457
467
|
assert_nil table_data.columns['username']
|
458
468
|
value(table_read(:users).columns['user_name']).must_equal({
|
@@ -499,7 +509,7 @@ describe Lhm do
|
|
499
509
|
end
|
500
510
|
end
|
501
511
|
|
502
|
-
|
512
|
+
replica do
|
503
513
|
table_data = table_read(:users)
|
504
514
|
assert_nil table_data.columns['fnord']
|
505
515
|
value(table_read(:users).columns['group']).must_equal({
|
@@ -525,7 +535,7 @@ describe Lhm do
|
|
525
535
|
t.remove_index('by')
|
526
536
|
end
|
527
537
|
|
528
|
-
|
538
|
+
replica do
|
529
539
|
value(table_read(:lines).columns).must_include 'by'
|
530
540
|
value(table_read(:lines).columns).wont_include 'lines'
|
531
541
|
value(index_on_columns?(:lines, ['between'], :unique)).must_equal true
|
@@ -554,7 +564,7 @@ describe Lhm do
|
|
554
564
|
|
555
565
|
insert.join
|
556
566
|
|
557
|
-
|
567
|
+
replica do
|
558
568
|
value(count_all(:users)).must_equal(60)
|
559
569
|
end
|
560
570
|
end
|
@@ -577,7 +587,7 @@ describe Lhm do
|
|
577
587
|
|
578
588
|
delete.join
|
579
589
|
|
580
|
-
|
590
|
+
replica do
|
581
591
|
value(count_all(:users)).must_equal(40)
|
582
592
|
end
|
583
593
|
end
|
@@ -645,13 +655,12 @@ describe Lhm do
|
|
645
655
|
log_lines = @logs.string.split("\n")
|
646
656
|
|
647
657
|
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
658
|
assert log_lines.one?{ |line| line.include?("LHM successfully reconnected to initial host")}
|
650
659
|
assert log_lines.one?{ |line| line.include?("100% complete")}
|
651
660
|
|
652
661
|
Lhm::ChunkInsert.remove_all_callbacks
|
653
662
|
|
654
|
-
|
663
|
+
replica do
|
655
664
|
value(count_all(:users)).must_equal(100)
|
656
665
|
end
|
657
666
|
end
|
@@ -31,7 +31,7 @@ describe Lhm::LockedSwitcher do
|
|
31
31
|
switcher = Lhm::LockedSwitcher.new(@migration, connection)
|
32
32
|
switcher.run
|
33
33
|
|
34
|
-
|
34
|
+
replica do
|
35
35
|
value(data_source_exists?(@origin)).must_equal true
|
36
36
|
value(table_read(@migration.archive_name).columns.keys).must_include 'origin'
|
37
37
|
end
|
@@ -41,7 +41,7 @@ describe Lhm::LockedSwitcher do
|
|
41
41
|
switcher = Lhm::LockedSwitcher.new(@migration, connection)
|
42
42
|
switcher.run
|
43
43
|
|
44
|
-
|
44
|
+
replica do
|
45
45
|
value(data_source_exists?(@destination)).must_equal false
|
46
46
|
value(table_read(@origin.name).columns.keys).must_include 'destination'
|
47
47
|
end
|
@@ -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,6 +61,7 @@ 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
67
|
lhm_retry = Lhm::SqlRetry.new(@connection, retry_options: {},
|
@@ -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
|
@@ -7,7 +7,7 @@ module ToxiproxyHelper
|
|
7
7
|
def included(base)
|
8
8
|
Toxiproxy.reset
|
9
9
|
|
10
|
-
# listen on localhost, but toxiproxy is in a container itself, thus the upstream uses the
|
10
|
+
# listen on localhost, but toxiproxy is in a container itself, thus the upstream uses the Podman-Compose DNS
|
11
11
|
Toxiproxy.populate(
|
12
12
|
[
|
13
13
|
{
|
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
|
data/spec/unit/printer_spec.rb
CHANGED
@@ -70,15 +70,11 @@ describe Lhm::Printer do
|
|
70
70
|
end
|
71
71
|
|
72
72
|
it 'prints the dots' do
|
73
|
-
mock =
|
74
|
-
|
75
|
-
mock.expect(:write, :return_value, ['.'])
|
76
|
-
end
|
73
|
+
mock = mock("output")
|
74
|
+
mock.expects(:write).with('.').times(10)
|
77
75
|
|
78
76
|
@printer.instance_variable_set(:@output, mock)
|
79
77
|
10.times { @printer.notify }
|
80
|
-
|
81
|
-
mock.verify
|
82
78
|
end
|
83
79
|
|
84
80
|
end
|
@@ -22,7 +22,7 @@ describe Lhm::SqlHelper do
|
|
22
22
|
end
|
23
23
|
|
24
24
|
it 'should quote column names in index specification' do
|
25
|
-
value(Lhm::SqlHelper.idx_spec(['title(10)', 'album']))
|
26
|
-
.must_equal('`title`(10), `album`')
|
25
|
+
value(Lhm::SqlHelper.idx_spec(['title(10)', 'name (6)', 'album']))
|
26
|
+
.must_equal('`title`(10), `name`(6), `album`')
|
27
27
|
end
|
28
28
|
end
|