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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 850cda6d2f610db2985c780d2a90d176e8520b373e21dafbe67df47e9a07ba0f
|
4
|
+
data.tar.gz: 982ab91c6319f5f7803d73d9f3734a57d28a13e76184500a665a5ab5b91d434f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8bdb254a7cd2091f7d1d125080bc843b43fc9039a7b2a99cba62752bee84726971e8018fb77738fdd0b15867a7f959ff419802270de75c08c2a72cd44d8b004b
|
7
|
+
data.tar.gz: b7d3a989384483ba9f43dd000f1e46c62805e15a0e15c4fcba0fedc964787d4fb1eafe294a2b23deae2d5b035057739551385eca08d24529d8c8e951ba44b163
|
data/.github/workflows/test.yml
CHANGED
@@ -14,30 +14,32 @@ jobs:
|
|
14
14
|
strategy:
|
15
15
|
fail-fast: false
|
16
16
|
matrix:
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
17
|
+
activerecord: ["6.1", "7.0", "7.1"]
|
18
|
+
ruby: ["3.0", "3.1", "3.2", "head"]
|
19
|
+
mysql: ["5.7", "8.0"]
|
20
|
+
adapter: ["mysql2", "trilogy"]
|
21
|
+
|
22
|
+
env:
|
23
|
+
BUNDLE_GEMFILE: "${{ github.workspace }}/gemfiles/activerecord_${{ matrix.activerecord }}.gemfile"
|
24
|
+
DATABASE_ADAPTER: "${{ matrix.adapter }}"
|
25
|
+
|
26
26
|
steps:
|
27
|
-
- uses: actions/checkout@
|
28
|
-
-
|
29
|
-
uses: ruby/setup-ruby@v1
|
27
|
+
- uses: actions/checkout@v4
|
28
|
+
- uses: ruby/setup-ruby@v1
|
30
29
|
with:
|
31
|
-
ruby-version: ${{matrix.ruby
|
32
|
-
bundler-cache:
|
33
|
-
|
34
|
-
run: BUNDLE_GEMFILE="${GITHUB_WORKSPACE}/gemfiles/activerecord_${{ matrix.activerecord-version }}.gemfile" bundle install
|
30
|
+
ruby-version: ${{matrix.ruby}}
|
31
|
+
bundler-cache: true
|
32
|
+
|
35
33
|
- name: Install Ubuntu packages
|
36
34
|
run: sudo apt-get update && sudo apt-get install numactl libaio-dev libmysqlclient-dev
|
35
|
+
|
37
36
|
- name: Setup MySQL and ProxySQL (docker-compose)
|
38
|
-
|
37
|
+
# Might have to change to docker compose up -d (i.e. Compose V2) when the Ubuntu image changes the docker-compose version
|
38
|
+
run: docker-compose -f docker-compose-mysql-${{ matrix.mysql }}.yml up -d
|
39
|
+
|
39
40
|
- name: Wait until DBs are alive
|
40
41
|
run: ./scripts/helpers/wait-for-dbs.sh
|
41
42
|
timeout-minutes: 2
|
43
|
+
|
42
44
|
- name: Run tests
|
43
|
-
run:
|
45
|
+
run: bundle exec rake specs
|
data/Appraisals
CHANGED
@@ -1,19 +1,13 @@
|
|
1
|
-
# First conflicted version
|
2
|
-
appraise "activerecord-6.0" do
|
3
|
-
gem "activerecord", "6.0.3"
|
4
|
-
end
|
5
|
-
|
6
|
-
# Second conflicted version
|
7
1
|
appraise "activerecord-6.1" do
|
8
2
|
gem "activerecord", "6.1.0"
|
3
|
+
gem "activerecord-trilogy-adapter"
|
9
4
|
end
|
10
5
|
|
11
|
-
# Latest version at the moment
|
12
6
|
appraise "activerecord-7.0" do
|
13
7
|
gem "activerecord", "7.0.8"
|
8
|
+
gem "activerecord-trilogy-adapter"
|
14
9
|
end
|
15
10
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
end
|
11
|
+
appraise "activerecord-7.1" do
|
12
|
+
gem "activerecord", "7.1.1"
|
13
|
+
end
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 4.1.0 (Oct, 2023)
|
4
|
+
* Test against MySQL 8.0.
|
5
|
+
* Test against Ruby Head.
|
6
|
+
* Drop support for ActiveRecord below version 6.1, as it has reached EOL.
|
7
|
+
* Add support for Trilogy MySQL client. It's works with built in ActiveRecord adapter from Rails 7.1 on, as well as dedicated one in older Rails versions.
|
8
|
+
|
3
9
|
# 4.0.0 (Sep, 2023)
|
4
10
|
* Deprecate `SlaveLag` throttler class name. Use `ReplicaLag` instead (https://github.com/Shopify/lhm/pull/144)
|
5
11
|
* Deprecate `slave_lag_throttler` throttler config value. Use `replica_lag_throttler` instead (https://github.com/Shopify/lhm/pull/144)
|
data/Gemfile.lock
CHANGED
@@ -1,35 +1,47 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
lhm-shopify (4.
|
4
|
+
lhm-shopify (4.1.0)
|
5
5
|
retriable (>= 3.0.0)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
activemodel (7.
|
11
|
-
activesupport (= 7.
|
12
|
-
activerecord (7.
|
13
|
-
activemodel (= 7.
|
14
|
-
activesupport (= 7.
|
15
|
-
|
10
|
+
activemodel (7.1.1)
|
11
|
+
activesupport (= 7.1.1)
|
12
|
+
activerecord (7.1.1)
|
13
|
+
activemodel (= 7.1.1)
|
14
|
+
activesupport (= 7.1.1)
|
15
|
+
timeout (>= 0.4.0)
|
16
|
+
activesupport (7.1.1)
|
17
|
+
base64
|
18
|
+
bigdecimal
|
16
19
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
20
|
+
connection_pool (>= 2.2.5)
|
21
|
+
drb
|
17
22
|
i18n (>= 1.6, < 2)
|
18
23
|
minitest (>= 5.1)
|
24
|
+
mutex_m
|
19
25
|
tzinfo (~> 2.0)
|
20
26
|
after_do (0.4.0)
|
21
27
|
appraisal (2.5.0)
|
22
28
|
bundler
|
23
29
|
rake
|
24
30
|
thor (>= 0.14.0)
|
31
|
+
base64 (0.1.1)
|
32
|
+
bigdecimal (3.1.4)
|
25
33
|
byebug (11.1.3)
|
26
34
|
concurrent-ruby (1.2.2)
|
35
|
+
connection_pool (2.4.1)
|
27
36
|
docile (1.4.0)
|
37
|
+
drb (2.1.1)
|
38
|
+
ruby2_keywords
|
28
39
|
i18n (1.14.1)
|
29
40
|
concurrent-ruby (~> 1.0)
|
30
41
|
minitest (5.20.0)
|
31
42
|
mocha (2.1.0)
|
32
43
|
ruby2_keywords (>= 0.0.5)
|
44
|
+
mutex_m (0.1.2)
|
33
45
|
mysql2 (0.5.5)
|
34
46
|
rake (13.0.6)
|
35
47
|
retriable (3.1.2)
|
@@ -41,7 +53,9 @@ GEM
|
|
41
53
|
simplecov-html (0.12.3)
|
42
54
|
simplecov_json_formatter (0.1.4)
|
43
55
|
thor (1.2.2)
|
56
|
+
timeout (0.4.0)
|
44
57
|
toxiproxy (2.0.2)
|
58
|
+
trilogy (2.6.0)
|
45
59
|
tzinfo (2.0.6)
|
46
60
|
concurrent-ruby (~> 1.0)
|
47
61
|
|
@@ -63,6 +77,7 @@ DEPENDENCIES
|
|
63
77
|
rake
|
64
78
|
simplecov
|
65
79
|
toxiproxy
|
80
|
+
trilogy
|
66
81
|
|
67
82
|
BUNDLED WITH
|
68
83
|
2.2.22
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
[](https://github.com/Shopify/lhm/actions/workflows/test.yml)
|
4
4
|
|
5
|
-
This is the Shopify fork of [SoundCloud's LHM](https://github.com/soundcloud/lhm). The
|
5
|
+
This is the Shopify fork of [SoundCloud's LHM](https://github.com/soundcloud/lhm). The
|
6
6
|
following description, originally from SoundCloud (with minor updates by Shopify),
|
7
7
|
gives some of the flavor around its original creation, and its choice of name...
|
8
8
|
|
@@ -112,7 +112,7 @@ tables must be cleaned up.
|
|
112
112
|
### Usage with ProxySQL
|
113
113
|
LHM can recover from connection loss. However, when used in conjunction with ProxySQL, there are multiple ways that
|
114
114
|
connection loss could induce data loss (if triggered by a failover). Therefore it will perform additional checks to
|
115
|
-
ensure that the MySQL host stays consistent across the schema migrations if the feature is enabled.
|
115
|
+
ensure that the MySQL host stays consistent across the schema migrations if the feature is enabled.
|
116
116
|
This is done by tagging every query with `/*maintenance:lhm*/`, which will be recognized by ProxySQL.
|
117
117
|
However, to get this feature working, a new ProxySQL query rule must be added.
|
118
118
|
```cnf
|
@@ -127,7 +127,7 @@ However, to get this feature working, a new ProxySQL query rule must be added.
|
|
127
127
|
This will ensure that all relevant queries are forwarded to the current writer.
|
128
128
|
|
129
129
|
Also, ProxySQL disables [multiplexing](https://proxysql.com/documentation/multiplexing/) for `select` on `@@` variables.
|
130
|
-
Therefore, the following rules must be added to ensure that queries (even if tagged with `/*maintenance:lhm*/`) get
|
130
|
+
Therefore, the following rules must be added to ensure that queries (even if tagged with `/*maintenance:lhm*/`) get
|
131
131
|
forwarded to the right target.
|
132
132
|
```cnf
|
133
133
|
{
|
@@ -144,7 +144,7 @@ forwarded to the right target.
|
|
144
144
|
}
|
145
145
|
```
|
146
146
|
|
147
|
-
Once these changes are added to the ProxySQL configuration (either through `.cnf` or dynamically through the admin interface),
|
147
|
+
Once these changes are added to the ProxySQL configuration (either through `.cnf` or dynamically through the admin interface),
|
148
148
|
the feature can be enabled. This is done by adding this flag when providing options to the migration:
|
149
149
|
```ruby
|
150
150
|
Lhm.change_table(..., options: {reconnect_with_consistent_host: true}) do |t|
|
@@ -285,7 +285,7 @@ To run the tests:
|
|
285
285
|
```bash
|
286
286
|
bundle exec rake unit # unit tests
|
287
287
|
bundle exec rake integration # integration tests
|
288
|
-
bundle exec rake
|
288
|
+
bundle exec rake specs # all tests
|
289
289
|
```
|
290
290
|
|
291
291
|
You can run an individual test as follows:
|
@@ -311,12 +311,12 @@ open coverage/index.html
|
|
311
311
|
```
|
312
312
|
|
313
313
|
### Merging for a new version
|
314
|
-
When creating a PR for a new version, make sure that th version has been bumped in `lib/lhm/version.rb`. Then run the following code snippet to ensure the everything is consistent, otherwise
|
314
|
+
When creating a PR for a new version, make sure that th version has been bumped in `lib/lhm/version.rb`. Then run the following code snippet to ensure the everything is consistent, otherwise
|
315
315
|
the gem will not publish.
|
316
316
|
```bash
|
317
317
|
bundle install
|
318
318
|
bundle update
|
319
|
-
bundle exec
|
319
|
+
bundle exec appraisal install
|
320
320
|
```
|
321
321
|
|
322
322
|
### Podman Compose
|
data/dev.yml
CHANGED
@@ -14,7 +14,7 @@ up:
|
|
14
14
|
meet: ":"
|
15
15
|
- custom:
|
16
16
|
name: Podman compose
|
17
|
-
met?: podman-compose
|
17
|
+
met?: podman-compose ps | grep -ioE -q "lhm.*running\(4\)"
|
18
18
|
meet: podman-compose up -d
|
19
19
|
- custom:
|
20
20
|
name: Waiting for DBs to be operational
|
@@ -41,6 +41,9 @@ commands:
|
|
41
41
|
run: podman-compose logs -f
|
42
42
|
clear:
|
43
43
|
run: podman-compose down -v && podman-compose up -d && ./scripts/helpers/wait-for-dbs.sh
|
44
|
+
subcommands:
|
45
|
+
mysql-5.7: podman-compose down -v && podman-compose -f docker-compose-mysql-5.7.yml up -d && ./scripts/helpers/wait-for-dbs.sh
|
46
|
+
mysql-8.0: podman-compose down -v && podman-compose -f docker-compose-mysql-8.0.yml up -d && ./scripts/helpers/wait-for-dbs.sh
|
44
47
|
pre-publish:
|
45
48
|
# Ensures all Gemfile.lock are sync with the new version in `lhm/version.rb` and runs appraisals
|
46
49
|
run: bundle install && bundle exec appraisal install && bundle exec appraisal rake specs
|
@@ -0,0 +1 @@
|
|
1
|
+
docker-compose.yml
|
@@ -0,0 +1,63 @@
|
|
1
|
+
services:
|
2
|
+
# Writer
|
3
|
+
mysql-1:
|
4
|
+
container_name: mysql-1
|
5
|
+
image: percona:8.0
|
6
|
+
platform: linux/amd64
|
7
|
+
command:
|
8
|
+
--server-id=1
|
9
|
+
--log-bin
|
10
|
+
--log-slave-updates=ON
|
11
|
+
--gtid-mode=ON
|
12
|
+
--enforce-gtid-consistency=ON
|
13
|
+
--read-only=OFF
|
14
|
+
--max-connections=1000
|
15
|
+
--default-authentication-plugin=mysql_native_password
|
16
|
+
hostname: 'mysql-1'
|
17
|
+
volumes:
|
18
|
+
- ./scripts/mysql/writer:/docker-entrypoint-initdb.d
|
19
|
+
environment:
|
20
|
+
MYSQL_ROOT_PASSWORD: password
|
21
|
+
MYSQL_HOST: mysql-1
|
22
|
+
ports:
|
23
|
+
- "13006:3306"
|
24
|
+
# Reader
|
25
|
+
mysql-2:
|
26
|
+
container_name: mysql-2
|
27
|
+
image: percona:8.0
|
28
|
+
platform: linux/amd64
|
29
|
+
command:
|
30
|
+
--server-id=2
|
31
|
+
--log-bin
|
32
|
+
--log-slave-updates=ON
|
33
|
+
--gtid-mode=ON
|
34
|
+
--enforce-gtid-consistency=ON
|
35
|
+
--read-only=ON
|
36
|
+
--max-connections=1000
|
37
|
+
--default-authentication-plugin=mysql_native_password
|
38
|
+
hostname: 'mysql-2'
|
39
|
+
volumes:
|
40
|
+
- ./scripts/mysql/reader:/docker-entrypoint-initdb.d
|
41
|
+
environment:
|
42
|
+
MYSQL_ROOT_PASSWORD: password
|
43
|
+
MYSQL_HOST: mysql-2
|
44
|
+
ports:
|
45
|
+
- "13007:3306"
|
46
|
+
# Proxysql
|
47
|
+
proxysql:
|
48
|
+
container_name: proxysql
|
49
|
+
image: proxysql/proxysql:2.0.11
|
50
|
+
platform: linux/amd64
|
51
|
+
volumes:
|
52
|
+
- ./scripts/proxysql/proxysql.cnf:/etc/proxysql.cnf
|
53
|
+
command: "proxysql -c /etc/proxysql.cnf -f --idle-threads"
|
54
|
+
ports:
|
55
|
+
- "13005:3306"
|
56
|
+
- "6032:6032"
|
57
|
+
toxiproxy:
|
58
|
+
container_name: toxiproxy
|
59
|
+
image: "ghcr.io/shopify/toxiproxy"
|
60
|
+
ports:
|
61
|
+
- "8474:8474"
|
62
|
+
- "22220:22220"
|
63
|
+
- "22222:22222"
|
data/docker-compose.yml
CHANGED
@@ -19,7 +19,7 @@ services:
|
|
19
19
|
MYSQL_ROOT_PASSWORD: password
|
20
20
|
MYSQL_HOST: mysql-1
|
21
21
|
ports:
|
22
|
-
- "
|
22
|
+
- "13006:3306"
|
23
23
|
# Reader
|
24
24
|
mysql-2:
|
25
25
|
container_name: mysql-2
|
@@ -40,7 +40,7 @@ services:
|
|
40
40
|
MYSQL_ROOT_PASSWORD: password
|
41
41
|
MYSQL_HOST: mysql-2
|
42
42
|
ports:
|
43
|
-
- "
|
43
|
+
- "13007:3306"
|
44
44
|
# Proxysql
|
45
45
|
proxysql:
|
46
46
|
container_name: proxysql
|
@@ -49,7 +49,7 @@ services:
|
|
49
49
|
- ./scripts/proxysql/proxysql.cnf:/etc/proxysql.cnf
|
50
50
|
command: "proxysql -c /etc/proxysql.cnf -f --idle-threads"
|
51
51
|
ports:
|
52
|
-
- "
|
52
|
+
- "13005:3306"
|
53
53
|
- "6032:6032"
|
54
54
|
toxiproxy:
|
55
55
|
container_name: toxiproxy
|
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ..
|
3
3
|
specs:
|
4
|
-
lhm-shopify (4.
|
4
|
+
lhm-shopify (4.1.0)
|
5
5
|
retriable (>= 3.0.0)
|
6
6
|
|
7
7
|
GEM
|
@@ -12,6 +12,9 @@ GEM
|
|
12
12
|
activerecord (6.1.0)
|
13
13
|
activemodel (= 6.1.0)
|
14
14
|
activesupport (= 6.1.0)
|
15
|
+
activerecord-trilogy-adapter (3.1.2)
|
16
|
+
activerecord (>= 6.0.a, < 7.1.a)
|
17
|
+
trilogy (>= 2.4.0)
|
15
18
|
activesupport (6.1.0)
|
16
19
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
17
20
|
i18n (>= 1.6, < 2)
|
@@ -43,9 +46,10 @@ GEM
|
|
43
46
|
simplecov_json_formatter (0.1.4)
|
44
47
|
thor (1.2.2)
|
45
48
|
toxiproxy (2.0.2)
|
49
|
+
trilogy (2.6.0)
|
46
50
|
tzinfo (2.0.6)
|
47
51
|
concurrent-ruby (~> 1.0)
|
48
|
-
zeitwerk (2.6.
|
52
|
+
zeitwerk (2.6.12)
|
49
53
|
|
50
54
|
PLATFORMS
|
51
55
|
arm64-darwin-21
|
@@ -55,6 +59,7 @@ PLATFORMS
|
|
55
59
|
|
56
60
|
DEPENDENCIES
|
57
61
|
activerecord (= 6.1.0)
|
62
|
+
activerecord-trilogy-adapter
|
58
63
|
after_do
|
59
64
|
appraisal
|
60
65
|
byebug
|
@@ -65,6 +70,7 @@ DEPENDENCIES
|
|
65
70
|
rake
|
66
71
|
simplecov
|
67
72
|
toxiproxy
|
73
|
+
trilogy
|
68
74
|
|
69
75
|
BUNDLED WITH
|
70
76
|
2.2.22
|
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ..
|
3
3
|
specs:
|
4
|
-
lhm-shopify (4.
|
4
|
+
lhm-shopify (4.1.0)
|
5
5
|
retriable (>= 3.0.0)
|
6
6
|
|
7
7
|
GEM
|
@@ -12,6 +12,9 @@ GEM
|
|
12
12
|
activerecord (7.0.8)
|
13
13
|
activemodel (= 7.0.8)
|
14
14
|
activesupport (= 7.0.8)
|
15
|
+
activerecord-trilogy-adapter (3.1.2)
|
16
|
+
activerecord (>= 6.0.a, < 7.1.a)
|
17
|
+
trilogy (>= 2.4.0)
|
15
18
|
activesupport (7.0.8)
|
16
19
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
17
20
|
i18n (>= 1.6, < 2)
|
@@ -42,6 +45,7 @@ GEM
|
|
42
45
|
simplecov_json_formatter (0.1.4)
|
43
46
|
thor (1.2.2)
|
44
47
|
toxiproxy (2.0.2)
|
48
|
+
trilogy (2.6.0)
|
45
49
|
tzinfo (2.0.6)
|
46
50
|
concurrent-ruby (~> 1.0)
|
47
51
|
|
@@ -53,6 +57,7 @@ PLATFORMS
|
|
53
57
|
|
54
58
|
DEPENDENCIES
|
55
59
|
activerecord (= 7.0.8)
|
60
|
+
activerecord-trilogy-adapter
|
56
61
|
after_do
|
57
62
|
appraisal
|
58
63
|
byebug
|
@@ -63,6 +68,7 @@ DEPENDENCIES
|
|
63
68
|
rake
|
64
69
|
simplecov
|
65
70
|
toxiproxy
|
71
|
+
trilogy
|
66
72
|
|
67
73
|
BUNDLED WITH
|
68
74
|
2.2.22
|
@@ -1,19 +1,19 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ..
|
3
3
|
specs:
|
4
|
-
lhm-shopify (4.
|
4
|
+
lhm-shopify (4.1.0)
|
5
5
|
retriable (>= 3.0.0)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
activemodel (7.1.
|
11
|
-
activesupport (= 7.1.
|
12
|
-
activerecord (7.1.
|
13
|
-
activemodel (= 7.1.
|
14
|
-
activesupport (= 7.1.
|
10
|
+
activemodel (7.1.1)
|
11
|
+
activesupport (= 7.1.1)
|
12
|
+
activerecord (7.1.1)
|
13
|
+
activemodel (= 7.1.1)
|
14
|
+
activesupport (= 7.1.1)
|
15
15
|
timeout (>= 0.4.0)
|
16
|
-
activesupport (7.1.
|
16
|
+
activesupport (7.1.1)
|
17
17
|
base64
|
18
18
|
bigdecimal
|
19
19
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
@@ -55,6 +55,7 @@ GEM
|
|
55
55
|
thor (1.2.2)
|
56
56
|
timeout (0.4.0)
|
57
57
|
toxiproxy (2.0.2)
|
58
|
+
trilogy (2.6.0)
|
58
59
|
tzinfo (2.0.6)
|
59
60
|
concurrent-ruby (~> 1.0)
|
60
61
|
|
@@ -65,7 +66,7 @@ PLATFORMS
|
|
65
66
|
x86_64-linux
|
66
67
|
|
67
68
|
DEPENDENCIES
|
68
|
-
activerecord (= 7.1.
|
69
|
+
activerecord (= 7.1.1)
|
69
70
|
after_do
|
70
71
|
appraisal
|
71
72
|
byebug
|
@@ -76,6 +77,7 @@ DEPENDENCIES
|
|
76
77
|
rake
|
77
78
|
simplecov
|
78
79
|
toxiproxy
|
80
|
+
trilogy
|
79
81
|
|
80
82
|
BUNDLED WITH
|
81
83
|
2.2.22
|
data/lhm.gemspec
CHANGED
@@ -31,6 +31,7 @@ Gem::Specification.new do |s|
|
|
31
31
|
s.add_development_dependency 'after_do'
|
32
32
|
s.add_development_dependency 'rake'
|
33
33
|
s.add_development_dependency 'mysql2'
|
34
|
+
s.add_development_dependency 'trilogy'
|
34
35
|
s.add_development_dependency 'simplecov'
|
35
36
|
s.add_development_dependency 'toxiproxy'
|
36
37
|
s.add_development_dependency 'appraisal'
|
data/lib/lhm/atomic_switcher.rb
CHANGED
@@ -26,14 +26,14 @@ module Lhm
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def atomic_switch
|
29
|
-
"
|
30
|
-
"`#{ @destination.name }`
|
29
|
+
"RENAME TABLE `#{ @origin.name }` TO `#{ @migration.archive_name }`, " \
|
30
|
+
"`#{ @destination.name }` TO `#{ @origin.name }`"
|
31
31
|
end
|
32
32
|
|
33
33
|
def validate
|
34
34
|
unless @connection.data_source_exists?(@origin.name) &&
|
35
35
|
@connection.data_source_exists?(@destination.name)
|
36
|
-
error "`#{ @origin.name }`
|
36
|
+
error "`#{ @origin.name }` AND `#{ @destination.name }` MUST EXIST"
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
data/lib/lhm/chunker.rb
CHANGED
@@ -79,9 +79,9 @@ module Lhm
|
|
79
79
|
private
|
80
80
|
|
81
81
|
def raise_on_non_pk_duplicate_warning
|
82
|
-
@connection.
|
83
|
-
unless
|
84
|
-
m = "Unexpected warning found for inserted row: #{
|
82
|
+
@connection.select_all("SHOW WARNINGS", should_retry: true, log_prefix: LOG_PREFIX).each do |row|
|
83
|
+
unless row["Message"].match?(/Duplicate entry .+ for key 'PRIMARY'/)
|
84
|
+
m = "Unexpected warning found for inserted row: #{row["Message"]}"
|
85
85
|
Lhm.logger.warn(m)
|
86
86
|
raise Error.new(m) if @raise_on_warnings
|
87
87
|
end
|
@@ -100,7 +100,7 @@ module Lhm
|
|
100
100
|
end
|
101
101
|
|
102
102
|
def upper_id(next_id, stride)
|
103
|
-
sql = "
|
103
|
+
sql = "SELECT id FROM `#{ @migration.origin_name }` WHERE id >= #{ next_id } ORDER BY id LIMIT 1 OFFSET #{ stride - 1}"
|
104
104
|
top = @connection.select_value(sql, should_retry: true, log_prefix: LOG_PREFIX)
|
105
105
|
|
106
106
|
[top ? top.to_i : @limit, @limit].min
|
data/lib/lhm/connection.rb
CHANGED
@@ -75,6 +75,14 @@ module Lhm
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
+
def select_all(query, should_retry: false, log_prefix: nil)
|
79
|
+
if should_retry
|
80
|
+
exec_with_retries(:select_all, query, log_prefix)
|
81
|
+
else
|
82
|
+
exec(:select_all, query)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
78
86
|
private
|
79
87
|
|
80
88
|
def exec(method, sql)
|
@@ -105,4 +113,4 @@ module Lhm
|
|
105
113
|
lhm_stack.at(first_candidate_index)
|
106
114
|
end
|
107
115
|
end
|
108
|
-
end
|
116
|
+
end
|
data/lib/lhm/sql_retry.rb
CHANGED
@@ -105,9 +105,7 @@ module Lhm
|
|
105
105
|
def mysql_single_value(name)
|
106
106
|
query = Lhm::ProxySQLHelper.tagged("SELECT #{name} LIMIT 1")
|
107
107
|
|
108
|
-
@connection.
|
109
|
-
return record&.first
|
110
|
-
end
|
108
|
+
@connection.select_value(query)
|
111
109
|
end
|
112
110
|
|
113
111
|
def same_host_as_initial?
|
@@ -140,32 +138,22 @@ module Lhm
|
|
140
138
|
log_with_prefix("Reconnected to wrong host. Started migration on: #{@initial_hostname} (server_id: #{@initial_server_id}), but reconnected to: #{hostname} (server_id: #{server_id}).", :error)
|
141
139
|
return false
|
142
140
|
end
|
143
|
-
rescue
|
141
|
+
rescue ActiveRecord::ConnectionNotEstablished
|
144
142
|
# Retry if ActiveRecord cannot reach host
|
145
|
-
next
|
143
|
+
next
|
144
|
+
rescue StandardError => e
|
146
145
|
log_with_prefix("Encountered error: [#{e.class}] #{e.message}. Will stop reconnection procedure.", :info)
|
147
146
|
return false
|
148
147
|
end
|
149
148
|
end
|
149
|
+
|
150
150
|
false
|
151
151
|
end
|
152
152
|
|
153
153
|
# For a full list of configuration options see https://github.com/kamui/retriable
|
154
154
|
def default_retry_config
|
155
155
|
{
|
156
|
-
on:
|
157
|
-
StandardError => [
|
158
|
-
/Lock wait timeout exceeded/,
|
159
|
-
/Timeout waiting for a response from the last query/,
|
160
|
-
/Deadlock found when trying to get lock/,
|
161
|
-
/Query execution was interrupted/,
|
162
|
-
/Lost connection to MySQL server during query/,
|
163
|
-
/Max connect timeout reached/,
|
164
|
-
/Unknown MySQL server host/,
|
165
|
-
/connection is locked to hostgroup/,
|
166
|
-
/The MySQL server is running with the --read-only option so it cannot execute this statement/,
|
167
|
-
]
|
168
|
-
},
|
156
|
+
on: retriable_mysql2_errors || retriable_trilogy_errors,
|
169
157
|
multiplier: 1, # each successive interval grows by this factor
|
170
158
|
base_interval: 1, # the initial interval in seconds between tries.
|
171
159
|
tries: 20, # Number of attempts to make at running your code block (includes initial attempt).
|
@@ -176,5 +164,35 @@ module Lhm
|
|
176
164
|
end
|
177
165
|
}.freeze
|
178
166
|
end
|
167
|
+
|
168
|
+
def retriable_mysql2_errors
|
169
|
+
return unless defined?(Mysql2::Error)
|
170
|
+
|
171
|
+
{
|
172
|
+
StandardError => [
|
173
|
+
/Lock wait timeout exceeded/,
|
174
|
+
/Timeout waiting for a response from the last query/,
|
175
|
+
/Deadlock found when trying to get lock/,
|
176
|
+
/Query execution was interrupted/,
|
177
|
+
/Lost connection to MySQL server during query/,
|
178
|
+
/Max connect timeout reached/,
|
179
|
+
/Unknown MySQL server host/,
|
180
|
+
/connection is locked to hostgroup/,
|
181
|
+
/The MySQL server is running with the --read-only option so it cannot execute this statement/,
|
182
|
+
],
|
183
|
+
}
|
184
|
+
end
|
185
|
+
|
186
|
+
def retriable_trilogy_errors
|
187
|
+
return unless defined?(Trilogy::BaseError)
|
188
|
+
|
189
|
+
{
|
190
|
+
ActiveRecord::StatementInvalid => [
|
191
|
+
# Those sometimes appear as Trilogy::TimeoutError, and sometimes as ActiveRecord::StatementInvalid
|
192
|
+
/Lock wait timeout exceeded/,
|
193
|
+
],
|
194
|
+
Trilogy::ConnectionError => nil,
|
195
|
+
}
|
196
|
+
end
|
179
197
|
end
|
180
198
|
end
|