lhm-shopify 4.0.0 → 4.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +20 -18
- data/Appraisals +5 -11
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +22 -7
- data/README.md +7 -7
- data/dev.yml +4 -1
- data/docker-compose-mysql-5.7.yml +1 -0
- data/docker-compose-mysql-8.0.yml +63 -0
- data/docker-compose.yml +3 -3
- data/gemfiles/activerecord_6.1.gemfile +1 -0
- data/gemfiles/activerecord_6.1.gemfile.lock +8 -2
- data/gemfiles/activerecord_7.0.gemfile +1 -0
- data/gemfiles/activerecord_7.0.gemfile.lock +7 -1
- data/gemfiles/{activerecord_6.0.gemfile → activerecord_7.1.gemfile} +1 -1
- data/gemfiles/{activerecord_7.1.0.beta1.gemfile.lock → activerecord_7.1.gemfile.lock} +10 -8
- data/lhm.gemspec +1 -0
- data/lib/lhm/atomic_switcher.rb +3 -3
- data/lib/lhm/chunker.rb +4 -4
- data/lib/lhm/connection.rb +9 -1
- data/lib/lhm/sql_retry.rb +36 -18
- data/lib/lhm/table.rb +3 -4
- data/lib/lhm/throttler/replica_lag.rb +17 -13
- data/lib/lhm/version.rb +1 -1
- data/scripts/helpers/wait-for-dbs.sh +3 -3
- data/scripts/mysql/writer/create_users.sql +1 -1
- data/spec/integration/atomic_switcher_spec.rb +4 -8
- data/spec/integration/chunker_spec.rb +21 -6
- data/spec/integration/database.yml +3 -3
- data/spec/integration/integration_helper.rb +11 -3
- data/spec/integration/lhm_spec.rb +29 -13
- data/spec/integration/proxysql_spec.rb +10 -10
- data/spec/integration/sql_retry/db_connection_helper.rb +2 -4
- data/spec/integration/sql_retry/lock_wait_spec.rb +7 -8
- data/spec/integration/sql_retry/lock_wait_timeout_test_helper.rb +18 -10
- data/spec/integration/sql_retry/proxysql_helper.rb +1 -1
- data/spec/integration/sql_retry/retry_with_proxysql_spec.rb +1 -2
- data/spec/integration/table_spec.rb +1 -1
- data/spec/test_helper.rb +27 -3
- data/spec/unit/atomic_switcher_spec.rb +2 -2
- data/spec/unit/chunker_spec.rb +43 -43
- data/spec/unit/connection_spec.rb +2 -2
- data/spec/unit/entangler_spec.rb +14 -24
- data/spec/unit/throttler/replica_lag_spec.rb +6 -14
- metadata +21 -8
- data/.travis.yml +0 -21
- data/gemfiles/activerecord_6.0.gemfile.lock +0 -71
- data/gemfiles/activerecord_7.1.0.beta1.gemfile +0 -7
data/spec/test_helper.rb
CHANGED
@@ -14,6 +14,7 @@ require 'after_do'
|
|
14
14
|
require 'byebug'
|
15
15
|
require 'pathname'
|
16
16
|
require 'lhm'
|
17
|
+
require 'active_record'
|
17
18
|
|
18
19
|
$project = Pathname.new(File.dirname(__FILE__) + '/..').cleanpath
|
19
20
|
$spec = $project.join('spec')
|
@@ -21,8 +22,31 @@ $fixtures = $spec.join('fixtures')
|
|
21
22
|
|
22
23
|
$db_name = 'test'
|
23
24
|
|
24
|
-
|
25
|
-
|
25
|
+
Database = Struct.new(:adapter, :client, :error_class, :timeout_error) do
|
26
|
+
def query(connection, sql)
|
27
|
+
results = connection.query(sql)
|
28
|
+
results = results.each_hash if adapter == "trilogy"
|
29
|
+
results
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
DATABASE =
|
34
|
+
case ENV['DATABASE_ADAPTER']
|
35
|
+
when 'trilogy'
|
36
|
+
require 'trilogy'
|
37
|
+
|
38
|
+
if ActiveRecord.version < ::Gem::Version.new('7.1.0')
|
39
|
+
require 'activerecord-trilogy-adapter'
|
40
|
+
require 'trilogy_adapter/connection'
|
41
|
+
|
42
|
+
ActiveRecord::Base.public_send :extend, TrilogyAdapter::Connection
|
43
|
+
end
|
44
|
+
|
45
|
+
Database.new('trilogy', Trilogy, Trilogy::BaseError, Trilogy::TimeoutError)
|
46
|
+
else
|
47
|
+
require 'mysql2'
|
48
|
+
Database.new('mysql2', Mysql2::Client, Mysql2::Error, Mysql2::Error::TimeoutError)
|
49
|
+
end
|
26
50
|
|
27
51
|
logger = Logger.new STDOUT
|
28
52
|
logger.level = Logger::WARN
|
@@ -53,7 +77,7 @@ end
|
|
53
77
|
|
54
78
|
def init_test_db
|
55
79
|
db_config = YAML.load_file(File.expand_path(File.dirname(__FILE__)) + '/integration/database.yml')
|
56
|
-
conn =
|
80
|
+
conn = DATABASE.client.new(
|
57
81
|
:host => '127.0.0.1',
|
58
82
|
:username => db_config['master']['user'],
|
59
83
|
:password => db_config['master']['password'],
|
@@ -21,8 +21,8 @@ describe Lhm::AtomicSwitcher do
|
|
21
21
|
describe 'atomic switch' do
|
22
22
|
it 'should perform a single atomic rename' do
|
23
23
|
value(@switcher.atomic_switch).must_equal(
|
24
|
-
"
|
25
|
-
'`destination`
|
24
|
+
"RENAME TABLE `origin` TO `#{ @migration.archive_name }`, " \
|
25
|
+
'`destination` TO `origin`'
|
26
26
|
)
|
27
27
|
end
|
28
28
|
end
|
data/spec/unit/chunker_spec.rb
CHANGED
@@ -20,7 +20,7 @@ describe Lhm::Chunker do
|
|
20
20
|
@destination = Lhm::Table.new('bar')
|
21
21
|
@migration = Lhm::Migration.new(@origin, @destination)
|
22
22
|
@connection = mock()
|
23
|
-
@connection.stubs(:
|
23
|
+
@connection.stubs(:select_value).returns("dummy")
|
24
24
|
# This is a poor man's stub
|
25
25
|
@throttler = Object.new
|
26
26
|
def @throttler.run
|
@@ -42,11 +42,11 @@ describe Lhm::Chunker do
|
|
42
42
|
5
|
43
43
|
end
|
44
44
|
|
45
|
-
@connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 4/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(7)
|
46
|
-
@connection.expects(:select_value).with(regexp_matches(/where id >= 8 order by id limit 1 offset 4/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(21)
|
47
|
-
@connection.expects(:update).with(regexp_matches(/between 1 and 7/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
48
|
-
@connection.expects(:update).with(regexp_matches(/between 8 and 10/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
49
|
-
@connection.expects(:
|
45
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 4/i), EXPECTED_RETRY_FLAGS_CHUNKER).returns(7)
|
46
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 8 order by id limit 1 offset 4/i), EXPECTED_RETRY_FLAGS_CHUNKER).returns(21)
|
47
|
+
@connection.expects(:update).with(regexp_matches(/between 1 and 7/i), EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
48
|
+
@connection.expects(:update).with(regexp_matches(/between 8 and 10/i), EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
49
|
+
@connection.expects(:select_all).twice.with(regexp_matches(/show warnings/i), EXPECTED_RETRY_FLAGS_CHUNKER).returns([])
|
50
50
|
|
51
51
|
@chunker.run
|
52
52
|
end
|
@@ -57,17 +57,17 @@ describe Lhm::Chunker do
|
|
57
57
|
2
|
58
58
|
end
|
59
59
|
|
60
|
-
@connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(2)
|
61
|
-
@connection.expects(:select_value).with(regexp_matches(/where id >= 3 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(4)
|
62
|
-
@connection.expects(:select_value).with(regexp_matches(/where id >= 5 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(6)
|
63
|
-
@connection.expects(:select_value).with(regexp_matches(/where id >= 7 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(8)
|
64
|
-
@connection.expects(:select_value).with(regexp_matches(/where id >= 9 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(10)
|
60
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/i), EXPECTED_RETRY_FLAGS_CHUNKER).returns(2)
|
61
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 3 order by id limit 1 offset 1/i), EXPECTED_RETRY_FLAGS_CHUNKER).returns(4)
|
62
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 5 order by id limit 1 offset 1/i), EXPECTED_RETRY_FLAGS_CHUNKER).returns(6)
|
63
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 7 order by id limit 1 offset 1/i), EXPECTED_RETRY_FLAGS_CHUNKER).returns(8)
|
64
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 9 order by id limit 1 offset 1/i), EXPECTED_RETRY_FLAGS_CHUNKER).returns(10)
|
65
65
|
|
66
|
-
@connection.expects(:update).with(regexp_matches(/between 1 and 2/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
67
|
-
@connection.expects(:update).with(regexp_matches(/between 3 and 4/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
68
|
-
@connection.expects(:update).with(regexp_matches(/between 5 and 6/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
69
|
-
@connection.expects(:update).with(regexp_matches(/between 7 and 8/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
70
|
-
@connection.expects(:update).with(regexp_matches(/between 9 and 10/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
66
|
+
@connection.expects(:update).with(regexp_matches(/between 1 and 2/i), EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
67
|
+
@connection.expects(:update).with(regexp_matches(/between 3 and 4/i), EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
68
|
+
@connection.expects(:update).with(regexp_matches(/between 5 and 6/i), EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
69
|
+
@connection.expects(:update).with(regexp_matches(/between 7 and 8/i), EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
70
|
+
@connection.expects(:update).with(regexp_matches(/between 9 and 10/i), EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
71
71
|
|
72
72
|
@chunker.run
|
73
73
|
end
|
@@ -84,17 +84,17 @@ describe Lhm::Chunker do
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
|
-
@connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(2)
|
88
|
-
@connection.expects(:select_value).with(regexp_matches(/where id >= 3 order by id limit 1 offset 2/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(5)
|
89
|
-
@connection.expects(:select_value).with(regexp_matches(/where id >= 6 order by id limit 1 offset 2/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(8)
|
90
|
-
@connection.expects(:select_value).with(regexp_matches(/where id >= 9 order by id limit 1 offset 2/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(nil)
|
87
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/i), EXPECTED_RETRY_FLAGS_CHUNKER).returns(2)
|
88
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 3 order by id limit 1 offset 2/i), EXPECTED_RETRY_FLAGS_CHUNKER).returns(5)
|
89
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 6 order by id limit 1 offset 2/i), EXPECTED_RETRY_FLAGS_CHUNKER).returns(8)
|
90
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 9 order by id limit 1 offset 2/i), EXPECTED_RETRY_FLAGS_CHUNKER).returns(nil)
|
91
91
|
|
92
|
-
@connection.expects(:update).with(regexp_matches(/between 1 and 2/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
93
|
-
@connection.expects(:update).with(regexp_matches(/between 3 and 5/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
94
|
-
@connection.expects(:update).with(regexp_matches(/between 6 and 8/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
95
|
-
@connection.expects(:update).with(regexp_matches(/between 9 and 10/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
92
|
+
@connection.expects(:update).with(regexp_matches(/between 1 and 2/i), EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
93
|
+
@connection.expects(:update).with(regexp_matches(/between 3 and 5/i), EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
94
|
+
@connection.expects(:update).with(regexp_matches(/between 6 and 8/i), EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
95
|
+
@connection.expects(:update).with(regexp_matches(/between 9 and 10/i), EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
96
96
|
|
97
|
-
@connection.expects(:
|
97
|
+
@connection.expects(:select_all).twice.with(regexp_matches(/show warnings/i), EXPECTED_RETRY_FLAGS_CHUNKER).returns([])
|
98
98
|
|
99
99
|
@chunker.run
|
100
100
|
end
|
@@ -104,8 +104,8 @@ describe Lhm::Chunker do
|
|
104
104
|
:start => 1,
|
105
105
|
:limit => 1)
|
106
106
|
|
107
|
-
@connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 0/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(nil)
|
108
|
-
@connection.expects(:update).with(regexp_matches(/between 1 and 1/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(1)
|
107
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 0/i), EXPECTED_RETRY_FLAGS_CHUNKER).returns(nil)
|
108
|
+
@connection.expects(:update).with(regexp_matches(/between 1 and 1/i), EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(1)
|
109
109
|
|
110
110
|
@chunker.run
|
111
111
|
end
|
@@ -118,17 +118,17 @@ describe Lhm::Chunker do
|
|
118
118
|
2
|
119
119
|
end
|
120
120
|
|
121
|
-
@connection.expects(:select_value).with(regexp_matches(/where id >= 2 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(3)
|
122
|
-
@connection.expects(:select_value).with(regexp_matches(/where id >= 4 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(5)
|
123
|
-
@connection.expects(:select_value).with(regexp_matches(/where id >= 6 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(7)
|
124
|
-
@connection.expects(:select_value).with(regexp_matches(/where id >= 8 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(9)
|
125
|
-
@connection.expects(:select_value).with(regexp_matches(/where id >= 10 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(nil)
|
121
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 2 order by id limit 1 offset 1/i), EXPECTED_RETRY_FLAGS_CHUNKER).returns(3)
|
122
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 4 order by id limit 1 offset 1/i), EXPECTED_RETRY_FLAGS_CHUNKER).returns(5)
|
123
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 6 order by id limit 1 offset 1/i), EXPECTED_RETRY_FLAGS_CHUNKER).returns(7)
|
124
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 8 order by id limit 1 offset 1/i), EXPECTED_RETRY_FLAGS_CHUNKER).returns(9)
|
125
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 10 order by id limit 1 offset 1/i), EXPECTED_RETRY_FLAGS_CHUNKER).returns(nil)
|
126
126
|
|
127
|
-
@connection.expects(:update).with(regexp_matches(/between 2 and 3/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
128
|
-
@connection.expects(:update).with(regexp_matches(/between 4 and 5/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
129
|
-
@connection.expects(:update).with(regexp_matches(/between 6 and 7/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
130
|
-
@connection.expects(:update).with(regexp_matches(/between 8 and 9/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
131
|
-
@connection.expects(:update).with(regexp_matches(/between 10 and 10/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(1)
|
127
|
+
@connection.expects(:update).with(regexp_matches(/between 2 and 3/i), EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
128
|
+
@connection.expects(:update).with(regexp_matches(/between 4 and 5/i), EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
129
|
+
@connection.expects(:update).with(regexp_matches(/between 6 and 7/i), EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
130
|
+
@connection.expects(:update).with(regexp_matches(/between 8 and 9/i), EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
|
131
|
+
@connection.expects(:update).with(regexp_matches(/between 10 and 10/i), EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(1)
|
132
132
|
|
133
133
|
@chunker.run
|
134
134
|
end
|
@@ -142,9 +142,9 @@ describe Lhm::Chunker do
|
|
142
142
|
2
|
143
143
|
end
|
144
144
|
|
145
|
-
@connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(2)
|
146
|
-
@connection.expects(:update).with(regexp_matches(/where \(foo.created_at > '2013-07-10' or foo.baz = 'quux'\) and `foo`/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(1)
|
147
|
-
@connection.expects(:
|
145
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/i), EXPECTED_RETRY_FLAGS_CHUNKER).returns(2)
|
146
|
+
@connection.expects(:update).with(regexp_matches(/where \(foo.created_at > '2013-07-10' or foo.baz = 'quux'\) and `foo`/i), EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(1)
|
147
|
+
@connection.expects(:select_all).with(regexp_matches(/show warnings/i), EXPECTED_RETRY_FLAGS_CHUNKER).returns([])
|
148
148
|
|
149
149
|
def @migration.conditions
|
150
150
|
"where foo.created_at > '2013-07-10' or foo.baz = 'quux'"
|
@@ -162,9 +162,9 @@ describe Lhm::Chunker do
|
|
162
162
|
2
|
163
163
|
end
|
164
164
|
|
165
|
-
@connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(2)
|
166
|
-
@connection.expects(:update).with(regexp_matches(/inner join bar on foo.id = bar.foo_id and/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(1)
|
167
|
-
@connection.expects(:
|
165
|
+
@connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/i), EXPECTED_RETRY_FLAGS_CHUNKER).returns(2)
|
166
|
+
@connection.expects(:update).with(regexp_matches(/inner join bar on foo.id = bar.foo_id and/i), EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(1)
|
167
|
+
@connection.expects(:select_all).with(regexp_matches(/show warnings/i), EXPECTED_RETRY_FLAGS_CHUNKER).returns([])
|
168
168
|
|
169
169
|
def @migration.conditions
|
170
170
|
'inner join bar on foo.id = bar.foo_id'
|
@@ -93,7 +93,7 @@ describe Lhm::Connection do
|
|
93
93
|
it "Queries should be tagged with ProxySQL tag if reconnect_with_consistent_host is enabled" do
|
94
94
|
ar_connection = mock()
|
95
95
|
ar_connection.expects(:public_send).with(:select_value, "SHOW TABLES #{Lhm::ProxySQLHelper::ANNOTATION}").returns("dummy")
|
96
|
-
ar_connection.stubs(:
|
96
|
+
ar_connection.stubs(:select_value).times(4).returns("dummy")
|
97
97
|
ar_connection.stubs(:active?).returns(true)
|
98
98
|
|
99
99
|
connection = Lhm::Connection.new(connection: ar_connection, options: {
|
@@ -108,4 +108,4 @@ describe Lhm::Connection do
|
|
108
108
|
|
109
109
|
assert_equal val, "dummy"
|
110
110
|
end
|
111
|
-
end
|
111
|
+
end
|
data/spec/unit/entangler_spec.rb
CHANGED
@@ -63,10 +63,9 @@ describe Lhm::Entangler do
|
|
63
63
|
it 'should retry trigger creation when it hits a lock wait timeout' do
|
64
64
|
tries = 1
|
65
65
|
ar_connection = mock()
|
66
|
+
ar_connection.stubs(:select_value).returns("dummy")
|
66
67
|
ar_connection.stubs(:execute)
|
67
|
-
|
68
|
-
.then
|
69
|
-
.raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction')
|
68
|
+
.raises(ActiveRecord::StatementInvalid, 'Lock wait timeout exceeded; try restarting transaction')
|
70
69
|
ar_connection.stubs(:active?).returns(true)
|
71
70
|
|
72
71
|
connection = Lhm::Connection.new(connection: ar_connection, options: {
|
@@ -79,15 +78,14 @@ describe Lhm::Entangler do
|
|
79
78
|
|
80
79
|
@entangler = Lhm::Entangler.new(@migration, connection)
|
81
80
|
|
82
|
-
assert_raises(
|
81
|
+
assert_raises(ActiveRecord::StatementInvalid) { @entangler.before }
|
83
82
|
end
|
84
83
|
|
85
84
|
it 'should not retry trigger creation with other mysql errors' do
|
86
85
|
ar_connection = mock()
|
86
|
+
ar_connection.stubs(:select_value).returns("dummy")
|
87
87
|
ar_connection.stubs(:execute)
|
88
|
-
.
|
89
|
-
.then
|
90
|
-
.raises(Mysql2::Error, 'The MySQL server is running with the --read-only option so it cannot execute this statement.')
|
88
|
+
.raises(DATABASE.error_class, 'The MySQL server is running with the --read-only option so it cannot execute this statement.')
|
91
89
|
ar_connection.stubs(:active?).returns(true)
|
92
90
|
connection = Lhm::Connection.new(connection: ar_connection, options: {
|
93
91
|
reconnect_with_consistent_host: true,
|
@@ -97,15 +95,14 @@ describe Lhm::Entangler do
|
|
97
95
|
})
|
98
96
|
|
99
97
|
@entangler = Lhm::Entangler.new(@migration, connection)
|
100
|
-
assert_raises(
|
98
|
+
assert_raises(DATABASE.error_class) { @entangler.before }
|
101
99
|
end
|
102
100
|
|
103
101
|
it 'should succesfully finish after retrying' do
|
104
102
|
ar_connection = mock()
|
103
|
+
ar_connection.stubs(:select_value).returns("dummy")
|
105
104
|
ar_connection.stubs(:execute)
|
106
|
-
.
|
107
|
-
.then
|
108
|
-
.raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction')
|
105
|
+
.raises(ActiveRecord::StatementInvalid, 'Lock wait timeout exceeded; try restarting transaction')
|
109
106
|
.then
|
110
107
|
.returns([["dummy"]])
|
111
108
|
ar_connection.stubs(:active?).returns(true)
|
@@ -124,22 +121,15 @@ describe Lhm::Entangler do
|
|
124
121
|
|
125
122
|
it 'should retry as many times as specified by configuration' do
|
126
123
|
ar_connection = mock()
|
124
|
+
ar_connection.stubs(:select_value).returns("dummy")
|
127
125
|
ar_connection.stubs(:execute)
|
128
|
-
.
|
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
|
126
|
+
.raises(DATABASE.error_class, 'Lock wait timeout exceeded; try restarting transaction')
|
137
127
|
.then
|
138
|
-
.raises(
|
128
|
+
.raises(DATABASE.error_class, 'Lock wait timeout exceeded; try restarting transaction')
|
139
129
|
.then
|
140
|
-
.
|
130
|
+
.raises(DATABASE.error_class, 'Lock wait timeout exceeded; try restarting transaction')
|
141
131
|
.then
|
142
|
-
.raises(
|
132
|
+
.raises(DATABASE.error_class, 'Lock wait timeout exceeded; try restarting transaction') # final error
|
143
133
|
ar_connection.stubs(:active?).returns(true)
|
144
134
|
|
145
135
|
connection = Lhm::Connection.new(connection: ar_connection, options: {
|
@@ -152,7 +142,7 @@ describe Lhm::Entangler do
|
|
152
142
|
|
153
143
|
@entangler = Lhm::Entangler.new(@migration, connection)
|
154
144
|
|
155
|
-
assert_raises(
|
145
|
+
assert_raises(DATABASE.error_class) { @entangler.before }
|
156
146
|
end
|
157
147
|
|
158
148
|
describe 'super long table names' do
|
@@ -43,14 +43,6 @@ describe Lhm::Throttler::Replica do
|
|
43
43
|
end
|
44
44
|
|
45
45
|
describe "#client" do
|
46
|
-
before do
|
47
|
-
class TestMysql2Client
|
48
|
-
def initialize(config)
|
49
|
-
raise Mysql2::Error.new("connection error")
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
46
|
describe 'on connection error' do
|
55
47
|
it 'logs and returns nil' do
|
56
48
|
assert_nil(Lhm::Throttler::Replica.new('replica', @dummy_mysql_client_config).connection)
|
@@ -58,14 +50,14 @@ describe Lhm::Throttler::Replica do
|
|
58
50
|
log_messages = @logs.string.lines
|
59
51
|
assert_equal(2, log_messages.length)
|
60
52
|
assert log_messages[0].include? "Connecting to replica on database: db"
|
61
|
-
assert log_messages[1].include? "Error connecting to replica
|
53
|
+
assert log_messages[1].include? "Error connecting to replica"
|
62
54
|
end
|
63
55
|
end
|
64
56
|
|
65
57
|
describe 'with proper config' do
|
66
|
-
it "creates a new
|
58
|
+
it "creates a new database client" do
|
67
59
|
expected_config = { username: 'user', password: 'pw', database: 'db', host: 'replica' }
|
68
|
-
|
60
|
+
DATABASE.client.stubs(:new).with(expected_config).returns(mock())
|
69
61
|
|
70
62
|
assert Lhm::Throttler::Replica.new('replica', @dummy_mysql_client_config).connection
|
71
63
|
end
|
@@ -80,7 +72,7 @@ describe Lhm::Throttler::Replica do
|
|
80
72
|
ActiveRecord::Base.stubs(:connection_pool).returns(stub(spec: stub(config: active_record_config)))
|
81
73
|
end
|
82
74
|
|
83
|
-
|
75
|
+
DATABASE.client.stubs(:new).returns(mock())
|
84
76
|
|
85
77
|
assert Lhm::Throttler::Replica.new('replica').connection
|
86
78
|
|
@@ -137,7 +129,7 @@ describe Lhm::Throttler::Replica do
|
|
137
129
|
describe "#lag on connection error" do
|
138
130
|
it "logs and returns 0 replica lag" do
|
139
131
|
client = mock()
|
140
|
-
client.stubs(:query).raises(
|
132
|
+
client.stubs(:query).raises(DATABASE.error_class, "Can't connect to MySQL server")
|
141
133
|
Lhm::Throttler::Replica.any_instance.stubs(:client).returns(client)
|
142
134
|
Lhm::Throttler::Replica.any_instance.stubs(:config).returns([])
|
143
135
|
|
@@ -224,7 +216,7 @@ describe Lhm::Throttler::ReplicaLag do
|
|
224
216
|
describe 'with MySQL stopped on the replica' do
|
225
217
|
it 'assumes 0 replica lag' do
|
226
218
|
client = mock()
|
227
|
-
client.stubs(:query).raises(
|
219
|
+
client.stubs(:query).raises(DATABASE.error_class, "Can't connect to MySQL server")
|
228
220
|
Lhm::Throttler::Replica.any_instance.stubs(:client).returns(client)
|
229
221
|
|
230
222
|
Lhm::Throttler::Replica.any_instance.stubs(:prepare_connection_config).returns([])
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lhm-shopify
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- SoundCloud
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2023-
|
15
|
+
date: 2023-10-12 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: retriable
|
@@ -112,6 +112,20 @@ dependencies:
|
|
112
112
|
- - ">="
|
113
113
|
- !ruby/object:Gem::Version
|
114
114
|
version: '0'
|
115
|
+
- !ruby/object:Gem::Dependency
|
116
|
+
name: trilogy
|
117
|
+
requirement: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
type: :development
|
123
|
+
prerelease: false
|
124
|
+
version_requirements: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
115
129
|
- !ruby/object:Gem::Dependency
|
116
130
|
name: simplecov
|
117
131
|
requirement: !ruby/object:Gem::Requirement
|
@@ -179,7 +193,6 @@ files:
|
|
179
193
|
- ".github/workflows/test.yml"
|
180
194
|
- ".gitignore"
|
181
195
|
- ".rubocop.yml"
|
182
|
-
- ".travis.yml"
|
183
196
|
- Appraisals
|
184
197
|
- CHANGELOG.md
|
185
198
|
- Gemfile
|
@@ -188,15 +201,15 @@ files:
|
|
188
201
|
- README.md
|
189
202
|
- Rakefile
|
190
203
|
- dev.yml
|
204
|
+
- docker-compose-mysql-5.7.yml
|
205
|
+
- docker-compose-mysql-8.0.yml
|
191
206
|
- docker-compose.yml
|
192
|
-
- gemfiles/activerecord_6.0.gemfile
|
193
|
-
- gemfiles/activerecord_6.0.gemfile.lock
|
194
207
|
- gemfiles/activerecord_6.1.gemfile
|
195
208
|
- gemfiles/activerecord_6.1.gemfile.lock
|
196
209
|
- gemfiles/activerecord_7.0.gemfile
|
197
210
|
- gemfiles/activerecord_7.0.gemfile.lock
|
198
|
-
- gemfiles/activerecord_7.1.
|
199
|
-
- gemfiles/activerecord_7.1.
|
211
|
+
- gemfiles/activerecord_7.1.gemfile
|
212
|
+
- gemfiles/activerecord_7.1.gemfile.lock
|
200
213
|
- lhm.gemspec
|
201
214
|
- lib/lhm-shopify.rb
|
202
215
|
- lib/lhm.rb
|
@@ -307,7 +320,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
307
320
|
- !ruby/object:Gem::Version
|
308
321
|
version: '0'
|
309
322
|
requirements: []
|
310
|
-
rubygems_version: 3.4.
|
323
|
+
rubygems_version: 3.4.20
|
311
324
|
signing_key:
|
312
325
|
specification_version: 4
|
313
326
|
summary: online schema changer for mysql
|
data/.travis.yml
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
before_script:
|
3
|
-
- "mysql -e 'create database lhm;'"
|
4
|
-
rvm:
|
5
|
-
- 2.0.0
|
6
|
-
- 2.1
|
7
|
-
- 2.2
|
8
|
-
sudo: false
|
9
|
-
gemfile:
|
10
|
-
- gemfiles/ar-2.3_mysql.gemfile
|
11
|
-
- gemfiles/ar-3.2_mysql.gemfile
|
12
|
-
- gemfiles/ar-3.2_mysql2.gemfile
|
13
|
-
- gemfiles/ar-4.0_mysql2.gemfile
|
14
|
-
- gemfiles/ar-4.1_mysql2.gemfile
|
15
|
-
- gemfiles/ar-4.2_mysql2.gemfile
|
16
|
-
matrix:
|
17
|
-
exclude:
|
18
|
-
- rvm: 2.2
|
19
|
-
gemfile: gemfiles/ar-3.2_mysql.gemfile
|
20
|
-
- rvm: 2.2
|
21
|
-
gemfile: gemfiles/ar-2.3_mysql.gemfile
|
@@ -1,71 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: ..
|
3
|
-
specs:
|
4
|
-
lhm-shopify (4.0.0)
|
5
|
-
retriable (>= 3.0.0)
|
6
|
-
|
7
|
-
GEM
|
8
|
-
remote: https://rubygems.org/
|
9
|
-
specs:
|
10
|
-
activemodel (6.0.3)
|
11
|
-
activesupport (= 6.0.3)
|
12
|
-
activerecord (6.0.3)
|
13
|
-
activemodel (= 6.0.3)
|
14
|
-
activesupport (= 6.0.3)
|
15
|
-
activesupport (6.0.3)
|
16
|
-
concurrent-ruby (~> 1.0, >= 1.0.2)
|
17
|
-
i18n (>= 0.7, < 2)
|
18
|
-
minitest (~> 5.1)
|
19
|
-
tzinfo (~> 1.1)
|
20
|
-
zeitwerk (~> 2.2, >= 2.2.2)
|
21
|
-
after_do (0.4.0)
|
22
|
-
appraisal (2.5.0)
|
23
|
-
bundler
|
24
|
-
rake
|
25
|
-
thor (>= 0.14.0)
|
26
|
-
byebug (11.1.3)
|
27
|
-
concurrent-ruby (1.2.2)
|
28
|
-
docile (1.4.0)
|
29
|
-
i18n (1.14.1)
|
30
|
-
concurrent-ruby (~> 1.0)
|
31
|
-
minitest (5.20.0)
|
32
|
-
mocha (2.1.0)
|
33
|
-
ruby2_keywords (>= 0.0.5)
|
34
|
-
mysql2 (0.5.5)
|
35
|
-
rake (13.0.6)
|
36
|
-
retriable (3.1.2)
|
37
|
-
ruby2_keywords (0.0.5)
|
38
|
-
simplecov (0.22.0)
|
39
|
-
docile (~> 1.1)
|
40
|
-
simplecov-html (~> 0.11)
|
41
|
-
simplecov_json_formatter (~> 0.1)
|
42
|
-
simplecov-html (0.12.3)
|
43
|
-
simplecov_json_formatter (0.1.4)
|
44
|
-
thor (1.2.2)
|
45
|
-
thread_safe (0.3.6)
|
46
|
-
toxiproxy (2.0.2)
|
47
|
-
tzinfo (1.2.11)
|
48
|
-
thread_safe (~> 0.1)
|
49
|
-
zeitwerk (2.6.11)
|
50
|
-
|
51
|
-
PLATFORMS
|
52
|
-
arm64-darwin-21
|
53
|
-
arm64-darwin-22
|
54
|
-
x86_64-darwin-20
|
55
|
-
x86_64-linux
|
56
|
-
|
57
|
-
DEPENDENCIES
|
58
|
-
activerecord (= 6.0.3)
|
59
|
-
after_do
|
60
|
-
appraisal
|
61
|
-
byebug
|
62
|
-
lhm-shopify!
|
63
|
-
minitest
|
64
|
-
mocha
|
65
|
-
mysql2
|
66
|
-
rake
|
67
|
-
simplecov
|
68
|
-
toxiproxy
|
69
|
-
|
70
|
-
BUNDLED WITH
|
71
|
-
2.2.22
|