lhm-shopify 3.3.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 +7 -0
- data/.github/workflows/test.yml +34 -0
- data/.gitignore +17 -0
- data/.rubocop.yml +183 -0
- data/.travis.yml +21 -0
- data/CHANGELOG.md +216 -0
- data/Gemfile +5 -0
- data/LICENSE +27 -0
- data/README.md +284 -0
- data/Rakefile +22 -0
- data/bin/.gitkeep +0 -0
- data/dbdeployer/config.json +32 -0
- data/dbdeployer/install.sh +64 -0
- data/dev.yml +20 -0
- data/gemfiles/ar-2.3_mysql.gemfile +6 -0
- data/gemfiles/ar-3.2_mysql.gemfile +5 -0
- data/gemfiles/ar-3.2_mysql2.gemfile +5 -0
- data/gemfiles/ar-4.0_mysql2.gemfile +5 -0
- data/gemfiles/ar-4.1_mysql2.gemfile +5 -0
- data/gemfiles/ar-4.2_mysql2.gemfile +5 -0
- data/gemfiles/ar-5.0_mysql2.gemfile +5 -0
- data/lhm.gemspec +34 -0
- data/lib/lhm.rb +131 -0
- data/lib/lhm/atomic_switcher.rb +52 -0
- data/lib/lhm/chunk_finder.rb +32 -0
- data/lib/lhm/chunk_insert.rb +51 -0
- data/lib/lhm/chunker.rb +87 -0
- data/lib/lhm/cleanup/current.rb +74 -0
- data/lib/lhm/command.rb +48 -0
- data/lib/lhm/entangler.rb +117 -0
- data/lib/lhm/intersection.rb +51 -0
- data/lib/lhm/invoker.rb +98 -0
- data/lib/lhm/locked_switcher.rb +74 -0
- data/lib/lhm/migration.rb +43 -0
- data/lib/lhm/migrator.rb +237 -0
- data/lib/lhm/printer.rb +59 -0
- data/lib/lhm/railtie.rb +9 -0
- data/lib/lhm/sql_helper.rb +77 -0
- data/lib/lhm/sql_retry.rb +61 -0
- data/lib/lhm/table.rb +121 -0
- data/lib/lhm/table_name.rb +23 -0
- data/lib/lhm/test_support.rb +35 -0
- data/lib/lhm/throttler.rb +36 -0
- data/lib/lhm/throttler/slave_lag.rb +145 -0
- data/lib/lhm/throttler/threads_running.rb +53 -0
- data/lib/lhm/throttler/time.rb +29 -0
- data/lib/lhm/timestamp.rb +11 -0
- data/lib/lhm/version.rb +6 -0
- data/shipit.rubygems.yml +0 -0
- data/spec/.lhm.example +4 -0
- data/spec/README.md +58 -0
- data/spec/fixtures/bigint_table.ddl +4 -0
- data/spec/fixtures/composite_primary_key.ddl +7 -0
- data/spec/fixtures/custom_primary_key.ddl +6 -0
- data/spec/fixtures/destination.ddl +6 -0
- data/spec/fixtures/lines.ddl +7 -0
- data/spec/fixtures/origin.ddl +6 -0
- data/spec/fixtures/permissions.ddl +5 -0
- data/spec/fixtures/small_table.ddl +4 -0
- data/spec/fixtures/tracks.ddl +5 -0
- data/spec/fixtures/users.ddl +14 -0
- data/spec/fixtures/wo_id_int_column.ddl +6 -0
- data/spec/integration/atomic_switcher_spec.rb +93 -0
- data/spec/integration/chunk_insert_spec.rb +29 -0
- data/spec/integration/chunker_spec.rb +185 -0
- data/spec/integration/cleanup_spec.rb +136 -0
- data/spec/integration/entangler_spec.rb +66 -0
- data/spec/integration/integration_helper.rb +237 -0
- data/spec/integration/invoker_spec.rb +33 -0
- data/spec/integration/lhm_spec.rb +585 -0
- data/spec/integration/lock_wait_timeout_spec.rb +30 -0
- data/spec/integration/locked_switcher_spec.rb +50 -0
- data/spec/integration/sql_retry/lock_wait_spec.rb +125 -0
- data/spec/integration/sql_retry/lock_wait_timeout_test_helper.rb +101 -0
- data/spec/integration/table_spec.rb +91 -0
- data/spec/test_helper.rb +32 -0
- data/spec/unit/atomic_switcher_spec.rb +31 -0
- data/spec/unit/chunk_finder_spec.rb +73 -0
- data/spec/unit/chunk_insert_spec.rb +44 -0
- data/spec/unit/chunker_spec.rb +166 -0
- data/spec/unit/entangler_spec.rb +124 -0
- data/spec/unit/intersection_spec.rb +51 -0
- data/spec/unit/lhm_spec.rb +29 -0
- data/spec/unit/locked_switcher_spec.rb +51 -0
- data/spec/unit/migrator_spec.rb +146 -0
- data/spec/unit/printer_spec.rb +97 -0
- data/spec/unit/sql_helper_spec.rb +32 -0
- data/spec/unit/table_name_spec.rb +39 -0
- data/spec/unit/table_spec.rb +47 -0
- data/spec/unit/throttler/slave_lag_spec.rb +317 -0
- data/spec/unit/throttler/threads_running_spec.rb +64 -0
- data/spec/unit/throttler_spec.rb +124 -0
- data/spec/unit/unit_helper.rb +13 -0
- metadata +239 -0
@@ -0,0 +1,585 @@
|
|
1
|
+
# Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
|
2
|
+
# Schmidt
|
3
|
+
|
4
|
+
require File.expand_path(File.dirname(__FILE__)) + '/integration_helper'
|
5
|
+
|
6
|
+
describe Lhm do
|
7
|
+
include IntegrationHelper
|
8
|
+
|
9
|
+
before(:each) { connect_master!; Lhm.cleanup(true) }
|
10
|
+
|
11
|
+
describe 'id column requirement' do
|
12
|
+
it 'should migrate the table when id is pk' do
|
13
|
+
table_create(:users)
|
14
|
+
|
15
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
16
|
+
t.add_column(:logins, "int(12) default '0'")
|
17
|
+
end
|
18
|
+
|
19
|
+
slave do
|
20
|
+
table_read(:users).columns['logins'].must_equal({
|
21
|
+
:type => 'int(12)',
|
22
|
+
:is_nullable => 'YES',
|
23
|
+
:column_default => '0',
|
24
|
+
:comment => '',
|
25
|
+
:collate => nil,
|
26
|
+
})
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should migrate the table when id is not pk' do
|
31
|
+
table_create(:custom_primary_key)
|
32
|
+
|
33
|
+
Lhm.change_table(:custom_primary_key, :atomic_switch => false) do |t|
|
34
|
+
t.add_column(:logins, "int(12) default '0'")
|
35
|
+
end
|
36
|
+
|
37
|
+
slave do
|
38
|
+
table_read(:custom_primary_key).columns['logins'].must_equal({
|
39
|
+
:type => 'int(12)',
|
40
|
+
:is_nullable => 'YES',
|
41
|
+
:column_default => '0',
|
42
|
+
:comment => '',
|
43
|
+
:collate => nil,
|
44
|
+
})
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should migrate the table when using a composite primary key if id column exists' do
|
49
|
+
table_create(:composite_primary_key)
|
50
|
+
|
51
|
+
Lhm.change_table(:composite_primary_key, :atomic_switch => false) do |t|
|
52
|
+
t.add_column(:logins, "int(12) default '0'")
|
53
|
+
end
|
54
|
+
|
55
|
+
slave do
|
56
|
+
table_read(:composite_primary_key).columns['logins'].must_equal({
|
57
|
+
:type => 'int(12)',
|
58
|
+
:is_nullable => 'YES',
|
59
|
+
:column_default => '0',
|
60
|
+
:comment => '',
|
61
|
+
:collate => nil,
|
62
|
+
})
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe 'changes' do
|
68
|
+
before(:each) do
|
69
|
+
table_create(:users)
|
70
|
+
table_create(:tracks)
|
71
|
+
table_create(:permissions)
|
72
|
+
end
|
73
|
+
|
74
|
+
describe 'when changing to a composite primary key' do
|
75
|
+
it 'should be able to use ddl statement to create composite keys' do
|
76
|
+
|
77
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
78
|
+
t.ddl("ALTER TABLE #{t.name} CHANGE id id bigint (20) NOT NULL")
|
79
|
+
t.ddl("ALTER TABLE #{t.name} DROP PRIMARY KEY, ADD PRIMARY KEY (username, id)")
|
80
|
+
t.ddl("ALTER TABLE #{t.name} ADD INDEX (id)")
|
81
|
+
t.ddl("ALTER TABLE #{t.name} CHANGE id id bigint (20) NOT NULL AUTO_INCREMENT")
|
82
|
+
end
|
83
|
+
|
84
|
+
slave do
|
85
|
+
connection.primary_key('users').must_equal(['username', 'id'])
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
describe 'when providing a subset of data to copy' do
|
92
|
+
|
93
|
+
before do
|
94
|
+
execute('insert into tracks set id = 13, public = 0')
|
95
|
+
11.times { |n| execute("insert into tracks set id = #{n + 1}, public = 1") }
|
96
|
+
11.times { |n| execute("insert into permissions set track_id = #{n + 1}") }
|
97
|
+
|
98
|
+
Lhm.change_table(:permissions, :atomic_switch => false) do |t|
|
99
|
+
t.filter('inner join tracks on tracks.`id` = permissions.`track_id` and tracks.`public` = 1')
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe 'when no additional data is inserted into the table' do
|
104
|
+
|
105
|
+
it 'migrates the existing data' do
|
106
|
+
slave do
|
107
|
+
count_all(:permissions).must_equal(11)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe 'when additional data is inserted' do
|
113
|
+
|
114
|
+
before do
|
115
|
+
execute('insert into tracks set id = 14, public = 0')
|
116
|
+
execute('insert into tracks set id = 15, public = 1')
|
117
|
+
execute('insert into permissions set track_id = 14')
|
118
|
+
execute('insert into permissions set track_id = 15')
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'migrates all data' do
|
122
|
+
slave do
|
123
|
+
count_all(:permissions).must_equal(13)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'should add a column' do
|
130
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
131
|
+
t.add_column(:logins, "INT(12) DEFAULT '0'")
|
132
|
+
end
|
133
|
+
|
134
|
+
slave do
|
135
|
+
table_read(:users).columns['logins'].must_equal({
|
136
|
+
:type => 'int(12)',
|
137
|
+
:is_nullable => 'YES',
|
138
|
+
:column_default => '0',
|
139
|
+
:comment => '',
|
140
|
+
:collate => nil,
|
141
|
+
})
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'should copy all rows' do
|
146
|
+
23.times { |n| execute("insert into users set reference = '#{ n }'") }
|
147
|
+
|
148
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
149
|
+
t.add_column(:logins, "INT(12) DEFAULT '0'")
|
150
|
+
end
|
151
|
+
|
152
|
+
slave do
|
153
|
+
count_all(:users).must_equal(23)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'should remove a column' do
|
158
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
159
|
+
t.remove_column(:comment)
|
160
|
+
end
|
161
|
+
|
162
|
+
slave do
|
163
|
+
assert_nil table_read(:users).columns['comment']
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'should add an index' do
|
168
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
169
|
+
t.add_index([:comment, :created_at])
|
170
|
+
end
|
171
|
+
|
172
|
+
slave do
|
173
|
+
index_on_columns?(:users, [:comment, :created_at]).must_equal(true)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'should add an index with a custom name' do
|
178
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
179
|
+
t.add_index([:comment, :created_at], :my_index_name)
|
180
|
+
end
|
181
|
+
|
182
|
+
slave do
|
183
|
+
index?(:users, :my_index_name).must_equal(true)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'should add an index on a column with a reserved name' do
|
188
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
189
|
+
t.add_index(:group)
|
190
|
+
end
|
191
|
+
|
192
|
+
slave do
|
193
|
+
index_on_columns?(:users, :group).must_equal(true)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'should add a unique index' do
|
198
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
199
|
+
t.add_unique_index(:comment)
|
200
|
+
end
|
201
|
+
|
202
|
+
slave do
|
203
|
+
index_on_columns?(:users, :comment, :unique).must_equal(true)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'should remove an index' do
|
208
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
209
|
+
t.remove_index([:username, :created_at])
|
210
|
+
end
|
211
|
+
|
212
|
+
slave do
|
213
|
+
index_on_columns?(:users, [:username, :created_at]).must_equal(false)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'should remove an index with a custom name' do
|
218
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
219
|
+
t.remove_index([:username, :group])
|
220
|
+
end
|
221
|
+
|
222
|
+
slave do
|
223
|
+
index?(:users, :index_with_a_custom_name).must_equal(false)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'should remove an index with a custom name by name' do
|
228
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
229
|
+
t.remove_index(:irrelevant_column_name, :index_with_a_custom_name)
|
230
|
+
end
|
231
|
+
|
232
|
+
slave do
|
233
|
+
index?(:users, :index_with_a_custom_name).must_equal(false)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'should apply a ddl statement' do
|
238
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
239
|
+
t.ddl('alter table %s add column flag tinyint(1)' % t.name)
|
240
|
+
end
|
241
|
+
|
242
|
+
slave do
|
243
|
+
table_read(:users).columns['flag'].must_equal({
|
244
|
+
:type => 'tinyint(1)',
|
245
|
+
:is_nullable => 'YES',
|
246
|
+
:column_default => nil,
|
247
|
+
:comment => '',
|
248
|
+
:collate => nil,
|
249
|
+
})
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'should change a column' do
|
254
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
255
|
+
t.change_column(:comment, "varchar(20) DEFAULT 'none' NOT NULL")
|
256
|
+
end
|
257
|
+
|
258
|
+
slave do
|
259
|
+
table_read(:users).columns['comment'].must_equal({
|
260
|
+
:type => 'varchar(20)',
|
261
|
+
:is_nullable => 'NO',
|
262
|
+
:column_default => 'none',
|
263
|
+
:comment => '',
|
264
|
+
:collate => 'utf8_general_ci',
|
265
|
+
})
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
it 'should change the last column in a table' do
|
270
|
+
table_create(:small_table)
|
271
|
+
|
272
|
+
Lhm.change_table(:small_table, :atomic_switch => false) do |t|
|
273
|
+
t.change_column(:id, 'int(5)')
|
274
|
+
end
|
275
|
+
|
276
|
+
slave do
|
277
|
+
table_read(:small_table).columns['id'].must_equal({
|
278
|
+
:type => 'int(5)',
|
279
|
+
:is_nullable => 'NO',
|
280
|
+
:column_default => nil,
|
281
|
+
:comment => '',
|
282
|
+
:collate => nil,
|
283
|
+
})
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
it 'should rename a column' do
|
288
|
+
table_create(:users)
|
289
|
+
|
290
|
+
execute("INSERT INTO users (username) VALUES ('a user')")
|
291
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
292
|
+
t.rename_column(:username, :login)
|
293
|
+
end
|
294
|
+
|
295
|
+
slave do
|
296
|
+
table_data = table_read(:users)
|
297
|
+
assert_nil table_data.columns['username']
|
298
|
+
table_read(:users).columns['login'].must_equal({
|
299
|
+
:type => 'varchar(255)',
|
300
|
+
:is_nullable => 'YES',
|
301
|
+
:column_default => nil,
|
302
|
+
:comment => '',
|
303
|
+
:collate => 'utf8_general_ci',
|
304
|
+
})
|
305
|
+
|
306
|
+
result = select_one('SELECT login from users')
|
307
|
+
result = result['login'] if result.respond_to?(:has_key?)
|
308
|
+
result.must_equal('a user')
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
it 'should rename a column with a default' do
|
313
|
+
table_create(:users)
|
314
|
+
|
315
|
+
execute("INSERT INTO users (username) VALUES ('a user')")
|
316
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
317
|
+
t.rename_column(:group, :fnord)
|
318
|
+
end
|
319
|
+
|
320
|
+
slave do
|
321
|
+
table_data = table_read(:users)
|
322
|
+
assert_nil table_data.columns['group']
|
323
|
+
table_read(:users).columns['fnord'].must_equal({
|
324
|
+
:type => 'varchar(255)',
|
325
|
+
:is_nullable => 'YES',
|
326
|
+
:column_default => 'Superfriends',
|
327
|
+
:comment => '',
|
328
|
+
:collate => 'utf8_general_ci',
|
329
|
+
})
|
330
|
+
|
331
|
+
result = select_one('SELECT `fnord` from users')
|
332
|
+
result = result['fnord'] if result.respond_to?(:has_key?)
|
333
|
+
result.must_equal('Superfriends')
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
it 'should rename a column with a collate' do
|
338
|
+
table_create(:users)
|
339
|
+
|
340
|
+
execute("ALTER TABLE users MODIFY `username` varchar(255) COLLATE utf8mb4_unicode_ci NULL")
|
341
|
+
execute("INSERT INTO users (username) VALUES ('a user')")
|
342
|
+
|
343
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
344
|
+
t.rename_column(:username, :user_name)
|
345
|
+
end
|
346
|
+
|
347
|
+
slave do
|
348
|
+
table_data = table_read(:users)
|
349
|
+
assert_nil table_data.columns['username']
|
350
|
+
table_read(:users).columns['user_name'].must_equal({
|
351
|
+
:type => 'varchar(255)',
|
352
|
+
:is_nullable => 'YES',
|
353
|
+
:column_default => nil,
|
354
|
+
:comment => '',
|
355
|
+
:collate => 'utf8mb4_unicode_ci',
|
356
|
+
})
|
357
|
+
|
358
|
+
result = select_one('SELECT `user_name` from users')
|
359
|
+
result = result['user_name'] if result.respond_to?(:has_key?)
|
360
|
+
result.must_equal('a user')
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
|
365
|
+
it 'should rename a column with a comment' do
|
366
|
+
table_create(:users)
|
367
|
+
|
368
|
+
execute("ALTER TABLE users MODIFY `reference` int(11) DEFAULT NULL COMMENT 'RefComment'")
|
369
|
+
execute("INSERT INTO users (username,reference) VALUES ('a user', 10)")
|
370
|
+
|
371
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
372
|
+
t.rename_column(:reference, :ref)
|
373
|
+
end
|
374
|
+
|
375
|
+
slave do
|
376
|
+
table_data = table_read(:users)
|
377
|
+
assert_nil table_data.columns['reference']
|
378
|
+
table_read(:users).columns['ref'].must_equal({
|
379
|
+
:type => 'int(11)',
|
380
|
+
:is_nullable => 'YES',
|
381
|
+
:column_default => nil,
|
382
|
+
:comment => 'RefComment',
|
383
|
+
:collate => nil,
|
384
|
+
})
|
385
|
+
|
386
|
+
result = select_one('SELECT `ref` from users')
|
387
|
+
result = result['ref'] if result.respond_to?(:has_key?)
|
388
|
+
result.must_equal(10)
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
it 'should rename a column with a default null' do
|
393
|
+
table_create(:users)
|
394
|
+
|
395
|
+
execute("ALTER TABLE users MODIFY `group` varchar(255) NULL DEFAULT NULL")
|
396
|
+
execute("INSERT INTO users (username) VALUES ('a user')")
|
397
|
+
|
398
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
399
|
+
t.rename_column(:group, :fnord)
|
400
|
+
end
|
401
|
+
|
402
|
+
slave do
|
403
|
+
table_data = table_read(:users)
|
404
|
+
assert_nil table_data.columns['group']
|
405
|
+
table_read(:users).columns['fnord'].must_equal({
|
406
|
+
:type => 'varchar(255)',
|
407
|
+
:is_nullable => 'YES',
|
408
|
+
:column_default => nil,
|
409
|
+
:comment => '',
|
410
|
+
:collate => 'utf8_general_ci',
|
411
|
+
})
|
412
|
+
|
413
|
+
result = select_one('SELECT `fnord` from users')
|
414
|
+
result = result['fnord'] if result.respond_to?(:has_key?)
|
415
|
+
result.must_equal(nil)
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
it 'should rename a colmn with nullable' do
|
420
|
+
table_create(:users)
|
421
|
+
execute("INSERT INTO users (username) VALUES ('a user')")
|
422
|
+
|
423
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
424
|
+
t.rename_column(:username, :user_name)
|
425
|
+
end
|
426
|
+
|
427
|
+
slave do
|
428
|
+
table_data = table_read(:users)
|
429
|
+
assert_nil table_data.columns['username']
|
430
|
+
table_read(:users).columns['user_name'].must_equal({
|
431
|
+
:type => 'varchar(255)',
|
432
|
+
:is_nullable => 'YES',
|
433
|
+
:column_default => nil,
|
434
|
+
:comment => '',
|
435
|
+
:collate => 'utf8_general_ci',
|
436
|
+
})
|
437
|
+
|
438
|
+
result = select_one('SELECT `user_name` from users')
|
439
|
+
result = result['user_name'] if result.respond_to?(:has_key?)
|
440
|
+
result.must_equal('a user')
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
it 'should rename a column with a not null' do
|
445
|
+
table_create(:users)
|
446
|
+
|
447
|
+
execute("ALTER TABLE users MODIFY username varchar(255) NOT NULL")
|
448
|
+
execute("INSERT INTO users (username) VALUES ('a user')")
|
449
|
+
|
450
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
451
|
+
t.rename_column(:username, :user_name)
|
452
|
+
end
|
453
|
+
|
454
|
+
slave do
|
455
|
+
table_data = table_read(:users)
|
456
|
+
assert_nil table_data.columns['username']
|
457
|
+
table_read(:users).columns['user_name'].must_equal({
|
458
|
+
:type => 'varchar(255)',
|
459
|
+
:is_nullable => 'NO',
|
460
|
+
:column_default => nil,
|
461
|
+
:comment => '',
|
462
|
+
:collate => 'utf8_general_ci',
|
463
|
+
})
|
464
|
+
|
465
|
+
result = select_one('SELECT `user_name` from users')
|
466
|
+
result = result['user_name'] if result.respond_to?(:has_key?)
|
467
|
+
result.must_equal('a user')
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
it 'should raise an exception if the triggers do not exist after copying all rows' do
|
472
|
+
table_create(:users)
|
473
|
+
|
474
|
+
execute("INSERT INTO users (username) VALUES ('a user')")
|
475
|
+
|
476
|
+
Lhm::Invoker.any_instance.stubs(:triggers_still_exist?).returns(false)
|
477
|
+
|
478
|
+
exception = assert_raises do
|
479
|
+
Lhm.change_table(:users) do |t|
|
480
|
+
t.rename_column(:group, :fnord)
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
assert_match "Verification failed, aborting early", exception.message
|
485
|
+
end
|
486
|
+
|
487
|
+
it 'should not perform the table rename if the triggers do not exist after copying all rows' do
|
488
|
+
table_create(:users)
|
489
|
+
|
490
|
+
execute("INSERT INTO users (username) VALUES ('a user')")
|
491
|
+
|
492
|
+
Lhm::Invoker.any_instance.stubs(:triggers_still_exist?).returns(false)
|
493
|
+
Lhm::LockedSwitcher.any_instance.expects(:run).never
|
494
|
+
|
495
|
+
assert_raises do
|
496
|
+
Lhm.change_table(:users) do |t|
|
497
|
+
t.rename_column(:group, :fnord)
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
slave do
|
502
|
+
table_data = table_read(:users)
|
503
|
+
assert_nil table_data.columns['fnord']
|
504
|
+
table_read(:users).columns['group'].must_equal({
|
505
|
+
:type => 'varchar(255)',
|
506
|
+
:is_nullable => 'YES',
|
507
|
+
:column_default => 'Superfriends',
|
508
|
+
:comment => '',
|
509
|
+
:collate => 'utf8_general_ci',
|
510
|
+
})
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
it 'works when mysql reserved words are used' do
|
515
|
+
table_create(:lines)
|
516
|
+
execute("insert into `lines` set id = 1, `between` = 'foo'")
|
517
|
+
execute("insert into `lines` set id = 2, `between` = 'bar'")
|
518
|
+
|
519
|
+
Lhm.change_table(:lines) do |t|
|
520
|
+
t.add_column('by', 'varchar(10)')
|
521
|
+
t.remove_column('lines')
|
522
|
+
t.add_index('by')
|
523
|
+
t.add_unique_index('between')
|
524
|
+
t.remove_index('by')
|
525
|
+
end
|
526
|
+
|
527
|
+
slave do
|
528
|
+
table_read(:lines).columns.must_include 'by'
|
529
|
+
table_read(:lines).columns.wont_include 'lines'
|
530
|
+
index_on_columns?(:lines, ['between'], :unique).must_equal true
|
531
|
+
index_on_columns?(:lines, ['by']).must_equal false
|
532
|
+
count_all(:lines).must_equal(2)
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
describe 'parallel' do
|
537
|
+
it 'should perserve inserts during migration' do
|
538
|
+
50.times { |n| execute("insert into users set reference = '#{ n }'") }
|
539
|
+
|
540
|
+
insert = Thread.new do
|
541
|
+
10.times do |n|
|
542
|
+
connect_master!
|
543
|
+
execute("insert into users set reference = '#{ 100 + n }'")
|
544
|
+
sleep(0.17)
|
545
|
+
end
|
546
|
+
end
|
547
|
+
sleep 2
|
548
|
+
|
549
|
+
options = { :stride => 10, :throttle => 97, :atomic_switch => false }
|
550
|
+
Lhm.change_table(:users, options) do |t|
|
551
|
+
t.add_column(:parallel, "INT(10) DEFAULT '0'")
|
552
|
+
end
|
553
|
+
|
554
|
+
insert.join
|
555
|
+
|
556
|
+
slave do
|
557
|
+
count_all(:users).must_equal(60)
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
it 'should perserve deletes during migration' do
|
562
|
+
50.times { |n| execute("insert into users set reference = '#{ n }'") }
|
563
|
+
|
564
|
+
delete = Thread.new do
|
565
|
+
10.times do |n|
|
566
|
+
execute("delete from users where reference = '#{ n }'")
|
567
|
+
sleep(0.17)
|
568
|
+
end
|
569
|
+
end
|
570
|
+
sleep 2
|
571
|
+
|
572
|
+
options = { :stride => 10, :throttle => 97, :atomic_switch => false }
|
573
|
+
Lhm.change_table(:users, options) do |t|
|
574
|
+
t.add_column(:parallel, "INT(10) DEFAULT '0'")
|
575
|
+
end
|
576
|
+
|
577
|
+
delete.join
|
578
|
+
|
579
|
+
slave do
|
580
|
+
count_all(:users).must_equal(40)
|
581
|
+
end
|
582
|
+
end
|
583
|
+
end
|
584
|
+
end
|
585
|
+
end
|