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,73 @@
|
|
1
|
+
# Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
|
2
|
+
# Schmidt
|
3
|
+
|
4
|
+
require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
|
5
|
+
|
6
|
+
describe Lhm::ChunkFinder do
|
7
|
+
before(:each) do
|
8
|
+
@origin = Lhm::Table.new('foo')
|
9
|
+
@destination = Lhm::Table.new('bar')
|
10
|
+
@migration = Lhm::Migration.new(@origin, @destination)
|
11
|
+
@connection = mock()
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#validate' do
|
15
|
+
describe 'when start is greater than limit' do
|
16
|
+
it 'raises' do
|
17
|
+
assert_raises { Lhm::ChunkFinder.new(@connection, @migration, {start: 2, limit: 1}).validate }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe 'when start is greater than limit' do
|
22
|
+
it 'does not raise' do
|
23
|
+
Lhm::ChunkFinder.new(@connection, @migration, {start: 1, limit: 2}).validate # does not raise
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#start' do
|
29
|
+
describe 'when initialized with 5' do
|
30
|
+
before(:each) do
|
31
|
+
@instance = Lhm::ChunkFinder.new(@connection, @migration, {start: 5, limit: 6})
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'returns 5' do
|
35
|
+
assert_equal @instance.start, 5
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'when initialized with nil and the min(id) is 22' do
|
40
|
+
before(:each) do
|
41
|
+
@connection.expects(:select_value).returns(22)
|
42
|
+
@instance = Lhm::ChunkFinder.new(@migration, @connection, {limit: 6})
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'returns 22' do
|
46
|
+
assert_equal @instance.start, 22
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#limit' do
|
52
|
+
describe 'when initialized with 6' do
|
53
|
+
before(:each) do
|
54
|
+
@instance = Lhm::ChunkFinder.new(@connection, @migration, {start: 5, limit: 6})
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'returns 6' do
|
58
|
+
assert_equal @instance.limit, 6
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'when initialized with nil and the max(id) is 33' do
|
63
|
+
before(:each) do
|
64
|
+
@connection.expects(:select_value).returns(33)
|
65
|
+
@instance = Lhm::ChunkFinder.new(@migration, @connection, {start: 5})
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'returns 33' do
|
69
|
+
assert_equal @instance.limit, 33
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
|
2
|
+
# Schmidt
|
3
|
+
|
4
|
+
require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
|
5
|
+
|
6
|
+
require 'lhm/chunk_insert'
|
7
|
+
|
8
|
+
describe Lhm::ChunkInsert do
|
9
|
+
before(:each) do
|
10
|
+
@connection = stub(:connection)
|
11
|
+
@origin = Lhm::Table.new('foo')
|
12
|
+
@destination = Lhm::Table.new('bar')
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#sql" do
|
16
|
+
describe "when migration has no conditions" do
|
17
|
+
before { @migration = Lhm::Migration.new(@origin, @destination) }
|
18
|
+
|
19
|
+
it "uses a simple where clause" do
|
20
|
+
assert_equal(
|
21
|
+
Lhm::ChunkInsert.new(@migration, @connection, 1, 2).sql,
|
22
|
+
"insert ignore into `bar` () select from `foo` where `foo`.`id` between 1 and 2"
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "when migration has a WHERE condition" do
|
28
|
+
before do
|
29
|
+
@migration = Lhm::Migration.new(
|
30
|
+
@origin,
|
31
|
+
@destination,
|
32
|
+
"where foo.created_at > '2013-07-10' or foo.baz = 'quux'"
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "combines the clause with the chunking WHERE condition" do
|
37
|
+
assert_equal(
|
38
|
+
Lhm::ChunkInsert.new(@migration, @connection, 1, 2).sql,
|
39
|
+
"insert ignore into `bar` () select from `foo` where (foo.created_at > '2013-07-10' or foo.baz = 'quux') and `foo`.`id` between 1 and 2"
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
# Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
|
2
|
+
# Schmidt
|
3
|
+
|
4
|
+
require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
|
5
|
+
|
6
|
+
require 'lhm/table'
|
7
|
+
require 'lhm/migration'
|
8
|
+
require 'lhm/chunker'
|
9
|
+
require 'lhm/throttler'
|
10
|
+
|
11
|
+
describe Lhm::Chunker do
|
12
|
+
include UnitHelper
|
13
|
+
|
14
|
+
before(:each) do
|
15
|
+
@origin = Lhm::Table.new('foo')
|
16
|
+
@destination = Lhm::Table.new('bar')
|
17
|
+
@migration = Lhm::Migration.new(@origin, @destination)
|
18
|
+
@connection = mock()
|
19
|
+
# This is a poor man's stub
|
20
|
+
@throttler = Object.new
|
21
|
+
def @throttler.run
|
22
|
+
# noop
|
23
|
+
end
|
24
|
+
def @throttler.stride
|
25
|
+
1
|
26
|
+
end
|
27
|
+
|
28
|
+
@chunker = Lhm::Chunker.new(@migration, @connection, :throttler => @throttler,
|
29
|
+
:start => 1,
|
30
|
+
:limit => 10)
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#run' do
|
34
|
+
|
35
|
+
it 'detects the max id to use in the chunk using the stride and use it if it is lower than the limit' do
|
36
|
+
def @throttler.stride
|
37
|
+
5
|
38
|
+
end
|
39
|
+
|
40
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 4/)).returns(7)
|
41
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 8 order by id limit 1 offset 4/)).returns(21)
|
42
|
+
@connection.expects(:update).with(regexp_matches(/between 1 and 7/)).returns(2)
|
43
|
+
@connection.expects(:update).with(regexp_matches(/between 8 and 10/)).returns(2)
|
44
|
+
|
45
|
+
@chunker.run
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
it 'chunks the result set according to the stride size' do
|
50
|
+
def @throttler.stride
|
51
|
+
2
|
52
|
+
end
|
53
|
+
|
54
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/)).returns(2)
|
55
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 3 order by id limit 1 offset 1/)).returns(4)
|
56
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 5 order by id limit 1 offset 1/)).returns(6)
|
57
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 7 order by id limit 1 offset 1/)).returns(8)
|
58
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 9 order by id limit 1 offset 1/)).returns(10)
|
59
|
+
|
60
|
+
@connection.expects(:update).with(regexp_matches(/between 1 and 2/)).returns(2)
|
61
|
+
@connection.expects(:update).with(regexp_matches(/between 3 and 4/)).returns(2)
|
62
|
+
@connection.expects(:update).with(regexp_matches(/between 5 and 6/)).returns(2)
|
63
|
+
@connection.expects(:update).with(regexp_matches(/between 7 and 8/)).returns(2)
|
64
|
+
@connection.expects(:update).with(regexp_matches(/between 9 and 10/)).returns(2)
|
65
|
+
|
66
|
+
@chunker.run
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'handles stride changes during execution' do
|
70
|
+
# roll our own stubbing
|
71
|
+
def @throttler.stride
|
72
|
+
@run_count ||= 0
|
73
|
+
@run_count = @run_count + 1
|
74
|
+
if @run_count > 1
|
75
|
+
3
|
76
|
+
else
|
77
|
+
2
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/)).returns(2)
|
82
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 3 order by id limit 1 offset 2/)).returns(5)
|
83
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 6 order by id limit 1 offset 2/)).returns(8)
|
84
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 9 order by id limit 1 offset 2/)).returns(nil)
|
85
|
+
|
86
|
+
@connection.expects(:update).with(regexp_matches(/between 1 and 2/)).returns(2)
|
87
|
+
@connection.expects(:update).with(regexp_matches(/between 3 and 5/)).returns(2)
|
88
|
+
@connection.expects(:update).with(regexp_matches(/between 6 and 8/)).returns(2)
|
89
|
+
@connection.expects(:update).with(regexp_matches(/between 9 and 10/)).returns(2)
|
90
|
+
|
91
|
+
@chunker.run
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'correctly copies single record tables' do
|
95
|
+
@chunker = Lhm::Chunker.new(@migration, @connection, :throttler => @throttler,
|
96
|
+
:start => 1,
|
97
|
+
:limit => 1)
|
98
|
+
|
99
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 0/)).returns(nil)
|
100
|
+
@connection.expects(:update).with(regexp_matches(/between 1 and 1/)).returns(1)
|
101
|
+
|
102
|
+
@chunker.run
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'copies the last record of a table, even it is the start of the last chunk' do
|
106
|
+
@chunker = Lhm::Chunker.new(@migration, @connection, :throttler => @throttler,
|
107
|
+
:start => 2,
|
108
|
+
:limit => 10)
|
109
|
+
def @throttler.stride
|
110
|
+
2
|
111
|
+
end
|
112
|
+
|
113
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 2 order by id limit 1 offset 1/)).returns(3)
|
114
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 4 order by id limit 1 offset 1/)).returns(5)
|
115
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 6 order by id limit 1 offset 1/)).returns(7)
|
116
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 8 order by id limit 1 offset 1/)).returns(9)
|
117
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 10 order by id limit 1 offset 1/)).returns(nil)
|
118
|
+
|
119
|
+
@connection.expects(:update).with(regexp_matches(/between 2 and 3/)).returns(2)
|
120
|
+
@connection.expects(:update).with(regexp_matches(/between 4 and 5/)).returns(2)
|
121
|
+
@connection.expects(:update).with(regexp_matches(/between 6 and 7/)).returns(2)
|
122
|
+
@connection.expects(:update).with(regexp_matches(/between 8 and 9/)).returns(2)
|
123
|
+
@connection.expects(:update).with(regexp_matches(/between 10 and 10/)).returns(1)
|
124
|
+
|
125
|
+
@chunker.run
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
it 'separates filter conditions from chunking conditions' do
|
130
|
+
@chunker = Lhm::Chunker.new(@migration, @connection, :throttler => @throttler,
|
131
|
+
:start => 1,
|
132
|
+
:limit => 2)
|
133
|
+
def @throttler.stride
|
134
|
+
2
|
135
|
+
end
|
136
|
+
|
137
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/)).returns(2)
|
138
|
+
@connection.expects(:update).with(regexp_matches(/where \(foo.created_at > '2013-07-10' or foo.baz = 'quux'\) and `foo`/)).returns(1)
|
139
|
+
|
140
|
+
def @migration.conditions
|
141
|
+
"where foo.created_at > '2013-07-10' or foo.baz = 'quux'"
|
142
|
+
end
|
143
|
+
|
144
|
+
@chunker.run
|
145
|
+
end
|
146
|
+
|
147
|
+
it "doesn't mess with inner join filters" do
|
148
|
+
@chunker = Lhm::Chunker.new(@migration, @connection, :throttler => @throttler,
|
149
|
+
:start => 1,
|
150
|
+
:limit => 2)
|
151
|
+
|
152
|
+
def @throttler.stride
|
153
|
+
2
|
154
|
+
end
|
155
|
+
|
156
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/)).returns(2)
|
157
|
+
@connection.expects(:update).with(regexp_matches(/inner join bar on foo.id = bar.foo_id and/)).returns(1)
|
158
|
+
|
159
|
+
def @migration.conditions
|
160
|
+
'inner join bar on foo.id = bar.foo_id'
|
161
|
+
end
|
162
|
+
|
163
|
+
@chunker.run
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
|
2
|
+
# Schmidt
|
3
|
+
|
4
|
+
require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
|
5
|
+
|
6
|
+
require 'lhm/table'
|
7
|
+
require 'lhm/migration'
|
8
|
+
require 'lhm/entangler'
|
9
|
+
|
10
|
+
describe Lhm::Entangler do
|
11
|
+
include UnitHelper
|
12
|
+
|
13
|
+
before(:each) do
|
14
|
+
@origin = Lhm::Table.new('origin')
|
15
|
+
@destination = Lhm::Table.new('destination')
|
16
|
+
@migration = Lhm::Migration.new(@origin, @destination)
|
17
|
+
@entangler = Lhm::Entangler.new(@migration)
|
18
|
+
end
|
19
|
+
|
20
|
+
describe 'activation' do
|
21
|
+
before(:each) do
|
22
|
+
@origin.columns['info'] = { :type => 'varchar(255)' }
|
23
|
+
@origin.columns['tags'] = { :type => 'varchar(255)' }
|
24
|
+
|
25
|
+
@destination.columns['info'] = { :type => 'varchar(255)' }
|
26
|
+
@destination.columns['tags'] = { :type => 'varchar(255)' }
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should create insert trigger to destination table' do
|
30
|
+
ddl = %Q{
|
31
|
+
create trigger `lhmt_ins_origin`
|
32
|
+
after insert on `origin` for each row
|
33
|
+
replace into `destination` (`info`, `tags`) /* large hadron migration */
|
34
|
+
values (`NEW`.`info`, `NEW`.`tags`)
|
35
|
+
}
|
36
|
+
|
37
|
+
@entangler.entangle.must_include strip(ddl)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should create an update trigger to the destination table' do
|
41
|
+
ddl = %Q{
|
42
|
+
create trigger `lhmt_upd_origin`
|
43
|
+
after update on `origin` for each row
|
44
|
+
replace into `destination` (`info`, `tags`) /* large hadron migration */
|
45
|
+
values (`NEW`.`info`, `NEW`.`tags`)
|
46
|
+
}
|
47
|
+
|
48
|
+
@entangler.entangle.must_include strip(ddl)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should create a delete trigger to the destination table' do
|
52
|
+
ddl = %Q{
|
53
|
+
create trigger `lhmt_del_origin`
|
54
|
+
after delete on `origin` for each row
|
55
|
+
delete ignore from `destination` /* large hadron migration */
|
56
|
+
where `destination`.`id` = OLD.`id`
|
57
|
+
}
|
58
|
+
|
59
|
+
@entangler.entangle.must_include strip(ddl)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should retry trigger creation when it hits a lock wait timeout' do
|
63
|
+
connection = mock()
|
64
|
+
tries = 1
|
65
|
+
@entangler = Lhm::Entangler.new(@migration, connection, retriable: {base_interval: 0, tries: tries})
|
66
|
+
connection.expects(:execute).times(tries).raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction')
|
67
|
+
|
68
|
+
assert_raises(Mysql2::Error) { @entangler.before }
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should not retry trigger creation with other mysql errors' do
|
72
|
+
connection = mock()
|
73
|
+
connection.expects(:execute).once.raises(Mysql2::Error, 'The MySQL server is running with the --read-only option so it cannot execute this statement.')
|
74
|
+
|
75
|
+
@entangler = Lhm::Entangler.new(@migration, connection, retriable: {base_interval: 0})
|
76
|
+
assert_raises(Mysql2::Error) { @entangler.before }
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should succesfully finish after retrying' do
|
80
|
+
connection = mock()
|
81
|
+
connection.stubs(:execute).raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction').then.returns(true)
|
82
|
+
@entangler = Lhm::Entangler.new(@migration, connection, retriable: {base_interval: 0})
|
83
|
+
|
84
|
+
assert @entangler.before
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should retry as many times as specified by configuration' do
|
88
|
+
connection = mock()
|
89
|
+
connection.expects(:execute).times(5).raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction')
|
90
|
+
@entangler = Lhm::Entangler.new(@migration, connection, retriable: {tries: 5, base_interval: 0})
|
91
|
+
|
92
|
+
assert_raises(Mysql2::Error) { @entangler.before }
|
93
|
+
end
|
94
|
+
|
95
|
+
describe 'super long table names' do
|
96
|
+
before(:each) do
|
97
|
+
@origin = Lhm::Table.new('a' * 64)
|
98
|
+
@destination = Lhm::Table.new('b' * 64)
|
99
|
+
@migration = Lhm::Migration.new(@origin, @destination)
|
100
|
+
@entangler = Lhm::Entangler.new(@migration)
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should use truncated names' do
|
104
|
+
@entangler.trigger(:ins).length.must_be :<=, 64
|
105
|
+
@entangler.trigger(:upd).length.must_be :<=, 64
|
106
|
+
@entangler.trigger(:del).length.must_be :<=, 64
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe 'removal' do
|
112
|
+
it 'should remove insert trigger' do
|
113
|
+
@entangler.untangle.must_include('drop trigger if exists `lhmt_ins_origin`')
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'should remove update trigger' do
|
117
|
+
@entangler.untangle.must_include('drop trigger if exists `lhmt_upd_origin`')
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'should remove delete trigger' do
|
121
|
+
@entangler.untangle.must_include('drop trigger if exists `lhmt_del_origin`')
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
|
2
|
+
# Schmidt
|
3
|
+
|
4
|
+
require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
|
5
|
+
|
6
|
+
require 'lhm/table'
|
7
|
+
require 'lhm/migrator'
|
8
|
+
|
9
|
+
describe Lhm::Intersection do
|
10
|
+
include UnitHelper
|
11
|
+
|
12
|
+
it 'should not have dropped changes' do
|
13
|
+
origin = Lhm::Table.new('origin')
|
14
|
+
origin.columns['dropped'] = varchar
|
15
|
+
origin.columns['retained'] = varchar
|
16
|
+
|
17
|
+
destination = Lhm::Table.new('destination')
|
18
|
+
destination.columns['retained'] = varchar
|
19
|
+
|
20
|
+
intersection = Lhm::Intersection.new(origin, destination)
|
21
|
+
intersection.destination.include?('dropped').must_equal(false)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should have unchanged columns' do
|
25
|
+
origin = Lhm::Table.new('origin')
|
26
|
+
origin.columns['dropped'] = varchar
|
27
|
+
origin.columns['retained'] = varchar
|
28
|
+
|
29
|
+
destination = Lhm::Table.new('destination')
|
30
|
+
destination.columns['retained'] = varchar
|
31
|
+
|
32
|
+
intersection = Lhm::Intersection.new(origin, destination)
|
33
|
+
intersection.destination.must_equal(['retained'])
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should have renamed columns' do
|
37
|
+
origin = Lhm::Table.new('origin')
|
38
|
+
origin.columns['old_name'] = varchar
|
39
|
+
|
40
|
+
destination = Lhm::Table.new('destination')
|
41
|
+
destination.columns['new_name'] = varchar
|
42
|
+
|
43
|
+
intersection = Lhm::Intersection.new(origin, destination, { 'old_name' => 'new_name' })
|
44
|
+
intersection.origin.must_equal(['old_name'])
|
45
|
+
intersection.destination.must_equal(['new_name'])
|
46
|
+
end
|
47
|
+
|
48
|
+
def varchar
|
49
|
+
{ :metadata => 'VARCHAR(255)' }
|
50
|
+
end
|
51
|
+
end
|