lhm-shopify 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +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
|