lhm-shopify 3.4.0 → 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/.github/workflows/test.yml +24 -15
- data/.gitignore +1 -6
- data/Appraisals +24 -0
- data/CHANGELOG.md +30 -0
- data/Gemfile.lock +66 -0
- data/README.md +55 -4
- data/Rakefile +11 -0
- data/dev.yml +31 -6
- data/docker-compose.yml +58 -0
- data/gemfiles/activerecord_5.2.gemfile +9 -0
- data/gemfiles/activerecord_5.2.gemfile.lock +65 -0
- data/gemfiles/activerecord_6.0.gemfile +7 -0
- data/gemfiles/activerecord_6.0.gemfile.lock +67 -0
- data/gemfiles/activerecord_6.1.gemfile +7 -0
- data/gemfiles/activerecord_6.1.gemfile.lock +66 -0
- data/gemfiles/activerecord_7.0.0.alpha2.gemfile +7 -0
- data/gemfiles/activerecord_7.0.0.alpha2.gemfile.lock +64 -0
- data/lhm.gemspec +7 -3
- data/lib/lhm/atomic_switcher.rb +5 -11
- data/lib/lhm/chunk_insert.rb +7 -10
- data/lib/lhm/chunker.rb +21 -10
- data/lib/lhm/cleanup/current.rb +9 -12
- data/lib/lhm/connection.rb +108 -0
- data/lib/lhm/entangler.rb +8 -13
- data/lib/lhm/invoker.rb +6 -4
- data/lib/lhm/locked_switcher.rb +2 -0
- data/lib/lhm/migrator.rb +2 -0
- data/lib/lhm/printer.rb +10 -6
- data/lib/lhm/proxysql_helper.rb +10 -0
- data/lib/lhm/sql_retry.rb +129 -10
- data/lib/lhm/throttler/slave_lag.rb +19 -2
- data/lib/lhm/version.rb +1 -1
- data/lib/lhm.rb +41 -16
- data/scripts/helpers/wait-for-dbs.sh +21 -0
- data/scripts/mysql/reader/create_replication.sql +10 -0
- data/scripts/mysql/writer/create_test_db.sql +1 -0
- data/scripts/mysql/writer/create_users.sql +6 -0
- data/scripts/proxysql/proxysql.cnf +117 -0
- data/spec/integration/atomic_switcher_spec.rb +53 -17
- data/spec/integration/chunk_insert_spec.rb +3 -2
- data/spec/integration/chunker_spec.rb +18 -16
- data/spec/integration/cleanup_spec.rb +49 -38
- data/spec/integration/database.yml +25 -0
- data/spec/integration/entangler_spec.rb +7 -5
- data/spec/integration/integration_helper.rb +25 -10
- data/spec/integration/lhm_spec.rb +114 -40
- data/spec/integration/lock_wait_timeout_spec.rb +2 -2
- data/spec/integration/locked_switcher_spec.rb +4 -4
- data/spec/integration/proxysql_spec.rb +34 -0
- data/spec/integration/sql_retry/db_connection_helper.rb +52 -0
- data/spec/integration/sql_retry/lock_wait_spec.rb +8 -6
- data/spec/integration/sql_retry/lock_wait_timeout_test_helper.rb +17 -4
- data/spec/integration/sql_retry/proxysql_helper.rb +22 -0
- data/spec/integration/sql_retry/retry_with_proxysql_spec.rb +109 -0
- data/spec/integration/table_spec.rb +11 -19
- data/spec/integration/toxiproxy_helper.rb +40 -0
- data/spec/test_helper.rb +24 -0
- data/spec/unit/atomic_switcher_spec.rb +4 -6
- data/spec/unit/chunk_insert_spec.rb +7 -2
- data/spec/unit/chunker_spec.rb +47 -42
- data/spec/unit/connection_spec.rb +111 -0
- data/spec/unit/entangler_spec.rb +85 -22
- data/spec/unit/intersection_spec.rb +4 -4
- data/spec/unit/lhm_spec.rb +23 -6
- data/spec/unit/locked_switcher_spec.rb +13 -18
- data/spec/unit/migrator_spec.rb +17 -19
- data/spec/unit/printer_spec.rb +14 -26
- data/spec/unit/sql_helper_spec.rb +8 -12
- data/spec/unit/table_spec.rb +5 -5
- data/spec/unit/throttler/slave_lag_spec.rb +14 -9
- data/spec/unit/throttler_spec.rb +12 -12
- data/spec/unit/unit_helper.rb +13 -0
- metadata +85 -14
- data/bin/.gitkeep +0 -0
- data/dbdeployer/config.json +0 -32
- data/dbdeployer/install.sh +0 -64
- data/gemfiles/ar-2.3_mysql.gemfile +0 -6
- data/gemfiles/ar-3.2_mysql.gemfile +0 -5
- data/gemfiles/ar-3.2_mysql2.gemfile +0 -5
- data/gemfiles/ar-4.0_mysql2.gemfile +0 -5
- data/gemfiles/ar-4.1_mysql2.gemfile +0 -5
- data/gemfiles/ar-4.2_mysql2.gemfile +0 -5
- data/gemfiles/ar-5.0_mysql2.gemfile +0 -5
data/spec/unit/entangler_spec.rb
CHANGED
@@ -6,6 +6,7 @@ require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
|
|
6
6
|
require 'lhm/table'
|
7
7
|
require 'lhm/migration'
|
8
8
|
require 'lhm/entangler'
|
9
|
+
require 'lhm/connection'
|
9
10
|
|
10
11
|
describe Lhm::Entangler do
|
11
12
|
include UnitHelper
|
@@ -34,7 +35,7 @@ describe Lhm::Entangler do
|
|
34
35
|
values (`NEW`.`info`, `NEW`.`tags`)
|
35
36
|
}
|
36
37
|
|
37
|
-
@entangler.entangle.must_include strip(ddl)
|
38
|
+
value(@entangler.entangle).must_include strip(ddl)
|
38
39
|
end
|
39
40
|
|
40
41
|
it 'should create an update trigger to the destination table' do
|
@@ -45,7 +46,7 @@ describe Lhm::Entangler do
|
|
45
46
|
values (`NEW`.`info`, `NEW`.`tags`)
|
46
47
|
}
|
47
48
|
|
48
|
-
@entangler.entangle.must_include strip(ddl)
|
49
|
+
value(@entangler.entangle).must_include strip(ddl)
|
49
50
|
end
|
50
51
|
|
51
52
|
it 'should create a delete trigger to the destination table' do
|
@@ -56,38 +57,100 @@ describe Lhm::Entangler do
|
|
56
57
|
where `destination`.`id` = OLD.`id`
|
57
58
|
}
|
58
59
|
|
59
|
-
@entangler.entangle.must_include strip(ddl)
|
60
|
+
value(@entangler.entangle).must_include strip(ddl)
|
60
61
|
end
|
61
62
|
|
62
63
|
it 'should retry trigger creation when it hits a lock wait timeout' do
|
63
|
-
connection = mock()
|
64
64
|
tries = 1
|
65
|
-
|
66
|
-
|
65
|
+
ar_connection = mock()
|
66
|
+
ar_connection.stubs(:execute)
|
67
|
+
.returns([["dummy"]], [["dummy"]], [["dummy"]])
|
68
|
+
.then
|
69
|
+
.raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction')
|
70
|
+
ar_connection.stubs(:active?).returns(true)
|
71
|
+
|
72
|
+
connection = Lhm::Connection.new(connection: ar_connection, options: {
|
73
|
+
reconnect_with_consistent_host: true,
|
74
|
+
retriable: {
|
75
|
+
base_interval: 0,
|
76
|
+
tries: tries
|
77
|
+
}
|
78
|
+
})
|
79
|
+
|
80
|
+
@entangler = Lhm::Entangler.new(@migration, connection)
|
67
81
|
|
68
82
|
assert_raises(Mysql2::Error) { @entangler.before }
|
69
83
|
end
|
70
84
|
|
71
85
|
it 'should not retry trigger creation with other mysql errors' do
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
86
|
+
ar_connection = mock()
|
87
|
+
ar_connection.stubs(:execute)
|
88
|
+
.returns([["dummy"]], [["dummy"]], [["dummy"]])
|
89
|
+
.then
|
90
|
+
.raises(Mysql2::Error, 'The MySQL server is running with the --read-only option so it cannot execute this statement.')
|
91
|
+
ar_connection.stubs(:active?).returns(true)
|
92
|
+
connection = Lhm::Connection.new(connection: ar_connection, options: {
|
93
|
+
reconnect_with_consistent_host: true,
|
94
|
+
retriable: {
|
95
|
+
base_interval: 0
|
96
|
+
},
|
97
|
+
})
|
98
|
+
|
99
|
+
@entangler = Lhm::Entangler.new(@migration, connection)
|
76
100
|
assert_raises(Mysql2::Error) { @entangler.before }
|
77
101
|
end
|
78
102
|
|
79
103
|
it 'should succesfully finish after retrying' do
|
80
|
-
|
81
|
-
|
82
|
-
|
104
|
+
ar_connection = mock()
|
105
|
+
ar_connection.stubs(:execute)
|
106
|
+
.returns([["dummy"]], [["dummy"]], [["dummy"]])
|
107
|
+
.then
|
108
|
+
.raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction')
|
109
|
+
.then
|
110
|
+
.returns([["dummy"]])
|
111
|
+
ar_connection.stubs(:active?).returns(true)
|
112
|
+
|
113
|
+
connection = Lhm::Connection.new(connection: ar_connection, options: {
|
114
|
+
reconnect_with_consistent_host: true,
|
115
|
+
retriable: {
|
116
|
+
base_interval: 0
|
117
|
+
},
|
118
|
+
})
|
119
|
+
|
120
|
+
@entangler = Lhm::Entangler.new(@migration, connection)
|
83
121
|
|
84
122
|
assert @entangler.before
|
85
123
|
end
|
86
124
|
|
87
125
|
it 'should retry as many times as specified by configuration' do
|
88
|
-
|
89
|
-
|
90
|
-
|
126
|
+
ar_connection = mock()
|
127
|
+
ar_connection.stubs(:execute)
|
128
|
+
.returns([["dummy"]], [["dummy"]], [["dummy"]]) # initial
|
129
|
+
.then
|
130
|
+
.raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction')
|
131
|
+
.then
|
132
|
+
.returns([["dummy"]]) # reconnect 1
|
133
|
+
.then
|
134
|
+
.raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction')
|
135
|
+
.then
|
136
|
+
.returns([["dummy"]]) # reconnect 2
|
137
|
+
.then
|
138
|
+
.raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction')
|
139
|
+
.then
|
140
|
+
.returns([["dummy"]]) # reconnect 3
|
141
|
+
.then
|
142
|
+
.raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction') # final error
|
143
|
+
ar_connection.stubs(:active?).returns(true)
|
144
|
+
|
145
|
+
connection = Lhm::Connection.new(connection: ar_connection, options: {
|
146
|
+
reconnect_with_consistent_host: true,
|
147
|
+
retriable: {
|
148
|
+
tries: 5,
|
149
|
+
base_interval: 0
|
150
|
+
},
|
151
|
+
})
|
152
|
+
|
153
|
+
@entangler = Lhm::Entangler.new(@migration, connection)
|
91
154
|
|
92
155
|
assert_raises(Mysql2::Error) { @entangler.before }
|
93
156
|
end
|
@@ -101,24 +164,24 @@ describe Lhm::Entangler do
|
|
101
164
|
end
|
102
165
|
|
103
166
|
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
|
167
|
+
value(@entangler.trigger(:ins).length).must_be :<=, 64
|
168
|
+
value(@entangler.trigger(:upd).length).must_be :<=, 64
|
169
|
+
value(@entangler.trigger(:del).length).must_be :<=, 64
|
107
170
|
end
|
108
171
|
end
|
109
172
|
end
|
110
173
|
|
111
174
|
describe 'removal' do
|
112
175
|
it 'should remove insert trigger' do
|
113
|
-
@entangler.untangle.must_include('drop trigger if exists `lhmt_ins_origin`')
|
176
|
+
value(@entangler.untangle).must_include('drop trigger if exists `lhmt_ins_origin`')
|
114
177
|
end
|
115
178
|
|
116
179
|
it 'should remove update trigger' do
|
117
|
-
@entangler.untangle.must_include('drop trigger if exists `lhmt_upd_origin`')
|
180
|
+
value(@entangler.untangle).must_include('drop trigger if exists `lhmt_upd_origin`')
|
118
181
|
end
|
119
182
|
|
120
183
|
it 'should remove delete trigger' do
|
121
|
-
@entangler.untangle.must_include('drop trigger if exists `lhmt_del_origin`')
|
184
|
+
value(@entangler.untangle).must_include('drop trigger if exists `lhmt_del_origin`')
|
122
185
|
end
|
123
186
|
end
|
124
187
|
end
|
@@ -18,7 +18,7 @@ describe Lhm::Intersection do
|
|
18
18
|
destination.columns['retained'] = varchar
|
19
19
|
|
20
20
|
intersection = Lhm::Intersection.new(origin, destination)
|
21
|
-
intersection.destination.include?('dropped').must_equal(false)
|
21
|
+
value(intersection.destination.include?('dropped')).must_equal(false)
|
22
22
|
end
|
23
23
|
|
24
24
|
it 'should have unchanged columns' do
|
@@ -30,7 +30,7 @@ describe Lhm::Intersection do
|
|
30
30
|
destination.columns['retained'] = varchar
|
31
31
|
|
32
32
|
intersection = Lhm::Intersection.new(origin, destination)
|
33
|
-
intersection.destination.must_equal(['retained'])
|
33
|
+
value(intersection.destination).must_equal(['retained'])
|
34
34
|
end
|
35
35
|
|
36
36
|
it 'should have renamed columns' do
|
@@ -41,8 +41,8 @@ describe Lhm::Intersection do
|
|
41
41
|
destination.columns['new_name'] = varchar
|
42
42
|
|
43
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'])
|
44
|
+
value(intersection.origin).must_equal(['old_name'])
|
45
|
+
value(intersection.destination).must_equal(['new_name'])
|
46
46
|
end
|
47
47
|
|
48
48
|
def varchar
|
data/spec/unit/lhm_spec.rb
CHANGED
@@ -11,9 +11,9 @@ describe Lhm do
|
|
11
11
|
describe 'logger' do
|
12
12
|
|
13
13
|
it 'should use the default parameters if no logger explicitly set' do
|
14
|
-
Lhm.logger.must_be_kind_of Logger
|
15
|
-
Lhm.logger.level.must_equal Logger::INFO
|
16
|
-
Lhm.logger.instance_eval { @logdev }.dev.must_equal STDOUT
|
14
|
+
value(Lhm.logger).must_be_kind_of Logger
|
15
|
+
value(Lhm.logger.level).must_equal Logger::INFO
|
16
|
+
value(Lhm.logger.instance_eval { @logdev }.dev).must_equal STDOUT
|
17
17
|
end
|
18
18
|
|
19
19
|
it 'should use s new logger if set' do
|
@@ -21,9 +21,26 @@ describe Lhm do
|
|
21
21
|
l.level = Logger::ERROR
|
22
22
|
Lhm.logger = l
|
23
23
|
|
24
|
-
Lhm.logger.level.must_equal Logger::ERROR
|
25
|
-
Lhm.logger.instance_eval { @logdev }.dev.must_be_kind_of File
|
26
|
-
Lhm.logger.instance_eval { @logdev }.dev.path.must_equal 'omg.ponies'
|
24
|
+
value(Lhm.logger.level).must_equal Logger::ERROR
|
25
|
+
value(Lhm.logger.instance_eval { @logdev }.dev).must_be_kind_of File
|
26
|
+
value(Lhm.logger.instance_eval { @logdev }.dev.path).must_equal 'omg.ponies'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'api' do
|
31
|
+
|
32
|
+
before(:each) do
|
33
|
+
@connection = mock()
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should create a new connection when calling setup' do
|
37
|
+
Lhm.setup(@connection)
|
38
|
+
value(Lhm.connection).must_be_kind_of(Lhm::Connection)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should create a new connection when none is created' do
|
42
|
+
ActiveRecord::Base.stubs(:connection).returns(@connection)
|
43
|
+
value(Lhm.connection).must_be_kind_of(Lhm::Connection)
|
27
44
|
end
|
28
45
|
end
|
29
46
|
end
|
@@ -20,32 +20,27 @@ describe Lhm::LockedSwitcher do
|
|
20
20
|
|
21
21
|
describe 'uncommitted' do
|
22
22
|
it 'should disable autocommit first' do
|
23
|
-
@switcher.
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
'set session autocommit = 0'
|
28
|
-
])
|
23
|
+
value(@switcher.statements[0..1]).must_equal([
|
24
|
+
'set @lhm_auto_commit = @@session.autocommit',
|
25
|
+
'set session autocommit = 0'
|
26
|
+
])
|
29
27
|
end
|
30
28
|
|
31
29
|
it 'should reapply original autocommit settings at the end' do
|
32
|
-
@switcher.
|
33
|
-
|
34
|
-
must_equal('set session autocommit = @lhm_auto_commit')
|
30
|
+
value(@switcher.statements[-1])
|
31
|
+
.must_equal('set session autocommit = @lhm_auto_commit')
|
35
32
|
end
|
36
33
|
end
|
37
34
|
|
38
35
|
describe 'switch' do
|
39
36
|
it 'should lock origin and destination table, switch, commit and unlock' do
|
40
|
-
@switcher.
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
'unlock tables'
|
48
|
-
])
|
37
|
+
value(@switcher.switch).must_equal([
|
38
|
+
'lock table `origin` write, `destination` write',
|
39
|
+
"alter table `origin` rename `#{ @migration.archive_name }`",
|
40
|
+
'alter table `destination` rename `origin`',
|
41
|
+
'commit',
|
42
|
+
'unlock tables'
|
43
|
+
])
|
49
44
|
end
|
50
45
|
end
|
51
46
|
end
|
data/spec/unit/migrator_spec.rb
CHANGED
@@ -18,7 +18,7 @@ describe Lhm::Migrator do
|
|
18
18
|
it 'should add an index' do
|
19
19
|
@creator.add_index(:a)
|
20
20
|
|
21
|
-
@creator.statements.must_equal([
|
21
|
+
value(@creator.statements).must_equal([
|
22
22
|
'create index `index_alt_on_a` on `lhmn_alt` (`a`)'
|
23
23
|
])
|
24
24
|
end
|
@@ -26,7 +26,7 @@ describe Lhm::Migrator do
|
|
26
26
|
it 'should add a composite index' do
|
27
27
|
@creator.add_index([:a, :b])
|
28
28
|
|
29
|
-
@creator.statements.must_equal([
|
29
|
+
value(@creator.statements).must_equal([
|
30
30
|
'create index `index_alt_on_a_and_b` on `lhmn_alt` (`a`, `b`)'
|
31
31
|
])
|
32
32
|
end
|
@@ -34,7 +34,7 @@ describe Lhm::Migrator do
|
|
34
34
|
it 'should add an index with prefix length' do
|
35
35
|
@creator.add_index(['a(10)', 'b'])
|
36
36
|
|
37
|
-
@creator.statements.must_equal([
|
37
|
+
value(@creator.statements).must_equal([
|
38
38
|
'create index `index_alt_on_a_and_b` on `lhmn_alt` (`a`(10), `b`)'
|
39
39
|
])
|
40
40
|
end
|
@@ -42,7 +42,7 @@ describe Lhm::Migrator do
|
|
42
42
|
it 'should add an index with a custom name' do
|
43
43
|
@creator.add_index([:a, :b], :custom_index_name)
|
44
44
|
|
45
|
-
@creator.statements.must_equal([
|
45
|
+
value(@creator.statements).must_equal([
|
46
46
|
'create index `custom_index_name` on `lhmn_alt` (`a`, `b`)'
|
47
47
|
])
|
48
48
|
end
|
@@ -56,7 +56,7 @@ describe Lhm::Migrator do
|
|
56
56
|
it 'should add a unique index' do
|
57
57
|
@creator.add_unique_index(['a(5)', :b])
|
58
58
|
|
59
|
-
@creator.statements.must_equal([
|
59
|
+
value(@creator.statements).must_equal([
|
60
60
|
'create unique index `index_alt_on_a_and_b` on `lhmn_alt` (`a`(5), `b`)'
|
61
61
|
])
|
62
62
|
end
|
@@ -64,7 +64,7 @@ describe Lhm::Migrator do
|
|
64
64
|
it 'should add a unique index with a custom name' do
|
65
65
|
@creator.add_unique_index([:a, :b], :custom_index_name)
|
66
66
|
|
67
|
-
@creator.statements.must_equal([
|
67
|
+
value(@creator.statements).must_equal([
|
68
68
|
'create unique index `custom_index_name` on `lhmn_alt` (`a`, `b`)'
|
69
69
|
])
|
70
70
|
end
|
@@ -78,7 +78,7 @@ describe Lhm::Migrator do
|
|
78
78
|
it 'should remove an index' do
|
79
79
|
@creator.remove_index(['b', 'a'])
|
80
80
|
|
81
|
-
@creator.statements.must_equal([
|
81
|
+
value(@creator.statements).must_equal([
|
82
82
|
'drop index `index_alt_on_b_and_a` on `lhmn_alt`'
|
83
83
|
])
|
84
84
|
end
|
@@ -86,7 +86,7 @@ describe Lhm::Migrator do
|
|
86
86
|
it 'should remove an index with a custom name' do
|
87
87
|
@creator.remove_index([:a, :b], :custom_index_name)
|
88
88
|
|
89
|
-
@creator.statements.must_equal([
|
89
|
+
value(@creator.statements).must_equal([
|
90
90
|
'drop index `custom_index_name` on `lhmn_alt`'
|
91
91
|
])
|
92
92
|
end
|
@@ -96,7 +96,7 @@ describe Lhm::Migrator do
|
|
96
96
|
it 'should add a column' do
|
97
97
|
@creator.add_column('logins', 'INT(12)')
|
98
98
|
|
99
|
-
@creator.statements.must_equal([
|
99
|
+
value(@creator.statements).must_equal([
|
100
100
|
'alter table `lhmn_alt` add column `logins` INT(12)'
|
101
101
|
])
|
102
102
|
end
|
@@ -104,7 +104,7 @@ describe Lhm::Migrator do
|
|
104
104
|
it 'should remove a column' do
|
105
105
|
@creator.remove_column('logins')
|
106
106
|
|
107
|
-
@creator.statements.must_equal([
|
107
|
+
value(@creator.statements).must_equal([
|
108
108
|
'alter table `lhmn_alt` drop `logins`'
|
109
109
|
])
|
110
110
|
end
|
@@ -112,7 +112,7 @@ describe Lhm::Migrator do
|
|
112
112
|
it 'should change a column' do
|
113
113
|
@creator.change_column('logins', 'INT(11)')
|
114
114
|
|
115
|
-
@creator.statements.must_equal([
|
115
|
+
value(@creator.statements).must_equal([
|
116
116
|
'alter table `lhmn_alt` modify column `logins` INT(11)'
|
117
117
|
])
|
118
118
|
end
|
@@ -122,7 +122,7 @@ describe Lhm::Migrator do
|
|
122
122
|
it 'should accept a ddl statement' do
|
123
123
|
@creator.ddl('alter table `%s` add column `f` tinyint(1)' % @creator.name)
|
124
124
|
|
125
|
-
@creator.statements.must_equal([
|
125
|
+
value(@creator.statements).must_equal([
|
126
126
|
'alter table `lhmn_alt` add column `f` tinyint(1)'
|
127
127
|
])
|
128
128
|
end
|
@@ -132,15 +132,13 @@ describe Lhm::Migrator do
|
|
132
132
|
it 'should add two columns' do
|
133
133
|
@creator.add_column('first', 'VARCHAR(64)')
|
134
134
|
@creator.add_column('last', 'VARCHAR(64)')
|
135
|
-
@creator.statements.length.must_equal(2)
|
135
|
+
value(@creator.statements.length).must_equal(2)
|
136
136
|
|
137
|
-
@creator.
|
138
|
-
|
139
|
-
must_equal('alter table `lhmn_alt` add column `first` VARCHAR(64)')
|
137
|
+
value(@creator.statements[0])
|
138
|
+
.must_equal('alter table `lhmn_alt` add column `first` VARCHAR(64)')
|
140
139
|
|
141
|
-
@creator.
|
142
|
-
|
143
|
-
must_equal('alter table `lhmn_alt` add column `last` VARCHAR(64)')
|
140
|
+
value(@creator.statements[1])
|
141
|
+
.must_equal('alter table `lhmn_alt` add column `last` VARCHAR(64)')
|
144
142
|
end
|
145
143
|
end
|
146
144
|
end
|
data/spec/unit/printer_spec.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
|
2
2
|
|
3
3
|
require 'lhm/printer'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
|
4
7
|
|
5
8
|
describe Lhm::Printer do
|
6
9
|
include UnitHelper
|
@@ -12,24 +15,17 @@ describe Lhm::Printer do
|
|
12
15
|
end
|
13
16
|
|
14
17
|
it 'prints the percentage' do
|
15
|
-
|
18
|
+
r, w = IO.pipe
|
19
|
+
Lhm.logger = Logger.new(w)
|
20
|
+
|
16
21
|
10.times do |i|
|
17
|
-
|
18
|
-
|
19
|
-
assert_match(/^\r/, message)
|
20
|
-
assert_match(/#{i}\/10/, message)
|
21
|
-
end
|
22
|
+
@printer.notify(i, 10)
|
23
|
+
assert_match(/#{i}\/10/, log_expression_message(r.gets))
|
22
24
|
end
|
23
|
-
|
24
|
-
@printer.instance_variable_set(:@output, mock)
|
25
|
-
10.times { |i| @printer.notify(i, 10) }
|
26
|
-
mock.verify
|
27
25
|
end
|
28
26
|
|
29
27
|
it 'always prints a bigger message' do
|
30
28
|
@length = 0
|
31
|
-
printer_mock = mock()
|
32
|
-
printer_mock.expects(:write).at_least_once
|
33
29
|
|
34
30
|
def assert_length(printer)
|
35
31
|
new_length = printer.instance_variable_get(:@max_length)
|
@@ -37,7 +33,6 @@ describe Lhm::Printer do
|
|
37
33
|
@length = new_length
|
38
34
|
end
|
39
35
|
|
40
|
-
@printer.instance_variable_set(:@output, printer_mock)
|
41
36
|
@printer.notify(10, 100)
|
42
37
|
assert_length(@printer)
|
43
38
|
@printer.notify(0, 100)
|
@@ -51,27 +46,20 @@ describe Lhm::Printer do
|
|
51
46
|
end
|
52
47
|
|
53
48
|
it 'prints the end message' do
|
54
|
-
|
55
|
-
|
56
|
-
mock.expect(:write, :return_value, ["\n"])
|
57
|
-
|
58
|
-
@printer.instance_variable_set(:@output, mock)
|
49
|
+
r, w = IO.pipe
|
50
|
+
Lhm.logger = Logger.new(w)
|
59
51
|
@printer.end
|
60
52
|
|
61
|
-
|
53
|
+
assert_equal(log_expression_message(r.gets), "100% complete\n")
|
62
54
|
end
|
63
55
|
|
64
56
|
it 'prints the exception message' do
|
65
|
-
|
66
|
-
|
67
|
-
mock.expect(:write, :return_value, ["\n"])
|
68
|
-
|
57
|
+
r, w = IO.pipe
|
58
|
+
Lhm.logger = Logger.new(w)
|
69
59
|
e = StandardError.new('woops')
|
70
|
-
|
71
|
-
@printer.instance_variable_set(:@output, mock)
|
72
60
|
@printer.exception(e)
|
73
61
|
|
74
|
-
|
62
|
+
assert_equal(log_expression_message(r.gets), "failed: #{e}\n")
|
75
63
|
end
|
76
64
|
end
|
77
65
|
|
@@ -7,26 +7,22 @@ require 'lhm/sql_helper'
|
|
7
7
|
|
8
8
|
describe Lhm::SqlHelper do
|
9
9
|
it 'should name index with a single column' do
|
10
|
-
Lhm::SqlHelper.
|
11
|
-
|
12
|
-
must_equal('index_users_on_name')
|
10
|
+
value(Lhm::SqlHelper.idx_name(:users, :name))
|
11
|
+
.must_equal('index_users_on_name')
|
13
12
|
end
|
14
13
|
|
15
14
|
it 'should name index with multiple columns' do
|
16
|
-
Lhm::SqlHelper.
|
17
|
-
|
18
|
-
must_equal('index_users_on_name_and_firstname')
|
15
|
+
value(Lhm::SqlHelper.idx_name(:users, [:name, :firstname]))
|
16
|
+
.must_equal('index_users_on_name_and_firstname')
|
19
17
|
end
|
20
18
|
|
21
19
|
it 'should name index with prefixed column' do
|
22
|
-
Lhm::SqlHelper.
|
23
|
-
|
24
|
-
must_equal('index_tracks_on_title_and_album')
|
20
|
+
value(Lhm::SqlHelper.idx_name(:tracks, ['title(10)', 'album']))
|
21
|
+
.must_equal('index_tracks_on_title_and_album')
|
25
22
|
end
|
26
23
|
|
27
24
|
it 'should quote column names in index specification' do
|
28
|
-
Lhm::SqlHelper.
|
29
|
-
|
30
|
-
must_equal('`title`(10), `album`')
|
25
|
+
value(Lhm::SqlHelper.idx_spec(['title(10)', 'album']))
|
26
|
+
.must_equal('`title`(10), `album`')
|
31
27
|
end
|
32
28
|
end
|
data/spec/unit/table_spec.rb
CHANGED
@@ -11,7 +11,7 @@ describe Lhm::Table do
|
|
11
11
|
describe 'names' do
|
12
12
|
it 'should name destination' do
|
13
13
|
@table = Lhm::Table.new('users')
|
14
|
-
@table.destination_name.must_equal 'lhmn_users'
|
14
|
+
value(@table.destination_name).must_equal 'lhmn_users'
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
@@ -23,25 +23,25 @@ describe Lhm::Table do
|
|
23
23
|
it 'should be satisfied with a single column primary key called id' do
|
24
24
|
@table = Lhm::Table.new('table', 'id')
|
25
25
|
set_columns(@table, { 'id' => { :type => 'int(1)' } })
|
26
|
-
@table.satisfies_id_column_requirement
|
26
|
+
value(@table.satisfies_id_column_requirement?).must_equal true
|
27
27
|
end
|
28
28
|
|
29
29
|
it 'should be satisfied with a primary key not called id, as long as there is still an id' do
|
30
30
|
@table = Lhm::Table.new('table', 'uuid')
|
31
31
|
set_columns(@table, { 'id' => { :type => 'int(1)' } })
|
32
|
-
@table.satisfies_id_column_requirement
|
32
|
+
value(@table.satisfies_id_column_requirement?).must_equal true
|
33
33
|
end
|
34
34
|
|
35
35
|
it 'should be satisifed if display attributes are not present (deprecated in mysql 8)' do
|
36
36
|
@table = Lhm::Table.new('table', 'id')
|
37
37
|
set_columns(@table, { 'id' => { :type => 'int' } })
|
38
|
-
@table.satisfies_id_column_requirement
|
38
|
+
value(@table.satisfies_id_column_requirement?).must_equal true
|
39
39
|
end
|
40
40
|
|
41
41
|
it 'should not be satisfied if id is not numeric' do
|
42
42
|
@table = Lhm::Table.new('table', 'id')
|
43
43
|
set_columns(@table, { 'id' => { :type => 'varchar(255)' } })
|
44
|
-
@table.satisfies_id_column_requirement
|
44
|
+
value(@table.satisfies_id_column_requirement?).must_equal false
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
@@ -39,7 +39,7 @@ describe Lhm::Throttler::Slave do
|
|
39
39
|
@logs = StringIO.new
|
40
40
|
Lhm.logger = Logger.new(@logs)
|
41
41
|
|
42
|
-
@dummy_mysql_client_config = lambda { {'username' => 'user', 'password' => 'pw', 'database' => 'db'} }
|
42
|
+
@dummy_mysql_client_config = lambda { { 'username' => 'user', 'password' => 'pw', 'database' => 'db' } }
|
43
43
|
end
|
44
44
|
|
45
45
|
describe "#client" do
|
@@ -64,7 +64,7 @@ describe Lhm::Throttler::Slave do
|
|
64
64
|
|
65
65
|
describe 'with proper config' do
|
66
66
|
it "creates a new Mysql2::Client" do
|
67
|
-
expected_config = {username: 'user', password: 'pw', database: 'db', host: 'slave'}
|
67
|
+
expected_config = { username: 'user', password: 'pw', database: 'db', host: 'slave' }
|
68
68
|
Mysql2::Client.stubs(:new).with(expected_config).returns(mock())
|
69
69
|
|
70
70
|
assert Lhm::Throttler::Slave.new('slave', @dummy_mysql_client_config).connection
|
@@ -73,8 +73,12 @@ describe Lhm::Throttler::Slave do
|
|
73
73
|
|
74
74
|
describe 'with active record config' do
|
75
75
|
it 'logs and creates client' do
|
76
|
-
active_record_config = {username: 'user', password: 'pw', database: 'db'}
|
77
|
-
ActiveRecord::
|
76
|
+
active_record_config = { username: 'user', password: 'pw', database: 'db' }
|
77
|
+
if ActiveRecord::VERSION::MAJOR > 6 || ActiveRecord::VERSION::MAJOR == 6 && ActiveRecord::VERSION::MINOR >= 1
|
78
|
+
ActiveRecord::Base.stubs(:connection_pool).returns(stub(db_config: stub(configuration_hash: active_record_config)))
|
79
|
+
else
|
80
|
+
ActiveRecord::Base.stubs(:connection_pool).returns(stub(spec: stub(config: active_record_config)))
|
81
|
+
end
|
78
82
|
|
79
83
|
Mysql2::Client.stubs(:new).returns(mock())
|
80
84
|
|
@@ -92,9 +96,9 @@ describe Lhm::Throttler::Slave do
|
|
92
96
|
class Connection
|
93
97
|
def self.query(query)
|
94
98
|
if query == Lhm::Throttler::Slave::SQL_SELECT_MAX_SLAVE_LAG
|
95
|
-
[{'Seconds_Behind_Master' => 20}]
|
99
|
+
[{ 'Seconds_Behind_Master' => 20 }]
|
96
100
|
elsif query == Lhm::Throttler::Slave::SQL_SELECT_SLAVE_HOSTS
|
97
|
-
[{'host' => '1.1.1.1:80'}]
|
101
|
+
[{ 'host' => '1.1.1.1:80' }]
|
98
102
|
end
|
99
103
|
end
|
100
104
|
end
|
@@ -104,7 +108,7 @@ describe Lhm::Throttler::Slave do
|
|
104
108
|
|
105
109
|
class StoppedConnection
|
106
110
|
def self.query(query)
|
107
|
-
[{'Seconds_Behind_Master' => nil}]
|
111
|
+
[{ 'Seconds_Behind_Master' => nil }]
|
108
112
|
end
|
109
113
|
end
|
110
114
|
|
@@ -138,7 +142,7 @@ describe Lhm::Throttler::Slave do
|
|
138
142
|
Lhm::Throttler::Slave.any_instance.stubs(:config).returns([])
|
139
143
|
|
140
144
|
slave = Lhm::Throttler::Slave.new('slave', @dummy_mysql_client_config)
|
141
|
-
|
145
|
+
Logger.any_instance.expects(:info).with("Unable to connect and/or query slave: Can't connect to MySQL server")
|
142
146
|
assert_equal(0, slave.lag)
|
143
147
|
end
|
144
148
|
end
|
@@ -286,7 +290,7 @@ describe Lhm::Throttler::SlaveLag do
|
|
286
290
|
describe 'with the :check_only option' do
|
287
291
|
describe 'with a callable argument' do
|
288
292
|
before do
|
289
|
-
check_only = lambda {{'host' => '1.1.1.3'}}
|
293
|
+
check_only = lambda { { 'host' => '1.1.1.3' } }
|
290
294
|
@throttler = Lhm::Throttler::SlaveLag.new :check_only => check_only
|
291
295
|
end
|
292
296
|
|
@@ -300,6 +304,7 @@ describe Lhm::Throttler::SlaveLag do
|
|
300
304
|
describe 'with a non-callable argument' do
|
301
305
|
before do
|
302
306
|
@throttler = Lhm::Throttler::SlaveLag.new :check_only => 'I cannot be called'
|
307
|
+
|
303
308
|
def @throttler.master_slave_hosts
|
304
309
|
['1.1.1.1', '1.1.1.4']
|
305
310
|
end
|