From b188aaef5fc999eb8c2af4ccb1da73d01fcfb445 Mon Sep 17 00:00:00 2001 From: Eric Yan Date: Tue, 18 Aug 2026 17:07:44 +0000 Subject: [PATCH 1/4] move-tables: support MariaDB GTID cutover resume --- go/logic/applier.go | 20 +++++++++- go/logic/migrator.go | 38 ++++++++++++------- go/logic/migrator_move_tables_cutover_test.go | 17 +++++++++ 3 files changed, 60 insertions(+), 15 deletions(-) diff --git a/go/logic/applier.go b/go/logic/applier.go index 7f705baff..05f678672 100644 --- a/go/logic/applier.go +++ b/go/logic/applier.go @@ -1472,9 +1472,25 @@ func (apl *Applier) ReadMoveTablesCutOverCheckpoint() (*Checkpoint, error) { return nil, err } chk.Timestamp = time.Unix(timestamp, 0) + sourceGTIDFlavor := "" + if apl.migrationContext.UseGTIDs && (coordStr != "" || drainGTIDStr != "") { + sourceVersion := apl.migrationContext.InspectorMySQLVersion + if sourceVersion == "" { + var err error + sourceVersion, err = mysql.GetDBVersion( + apl.migrationContext.Uuid, + apl.migrationContext.InspectorConnectionConfig.GetDBUri(apl.migrationContext.DatabaseName), + ) + if err != nil { + return nil, err + } + apl.migrationContext.InspectorMySQLVersion = sourceVersion + } + sourceGTIDFlavor = mysql.FlavorFor(sourceVersion) + } if coordStr != "" { if apl.migrationContext.UseGTIDs { - coords, err := mysql.NewGTIDBinlogCoordinates(mysql.FlavorFor(apl.migrationContext.InspectorMySQLVersion), coordStr) + coords, err := mysql.NewGTIDBinlogCoordinates(sourceGTIDFlavor, coordStr) if err != nil { return nil, err } @@ -1488,7 +1504,7 @@ func (apl *Applier) ReadMoveTablesCutOverCheckpoint() (*Checkpoint, error) { } } if drainGTIDStr != "" { - drainGTID, err := mysql.NewGTIDBinlogCoordinates(mysql.FlavorFor(apl.migrationContext.InspectorMySQLVersion), drainGTIDStr) + drainGTID, err := mysql.NewGTIDBinlogCoordinates(sourceGTIDFlavor, drainGTIDStr) if err != nil { return nil, err } diff --git a/go/logic/migrator.go b/go/logic/migrator.go index d5d5a6a6a..1e1bc30b9 100644 --- a/go/logic/migrator.go +++ b/go/logic/migrator.go @@ -1448,12 +1448,14 @@ func (mgtr *Migrator) moveTablesCutOver() (err error) { sql.EscapeName(sourceDB), sql.EscapeName(tableName), sql.EscapeName(sourceDB), sql.EscapeName(delTable))) } - renameAndCaptureQuery := fmt.Sprintf("rename /* gh-ost */ table %s;\nselect @@global.gtid_executed", - strings.Join(renameClauses, ", ")) + drainGTIDVariable := moveTablesDrainGTIDVariable(mgtr.migrationContext.InspectorMySQLVersion) + renameAndCaptureQuery := fmt.Sprintf("rename /* gh-ost */ table %s;\nselect %s", + strings.Join(renameClauses, ", "), drainGTIDVariable) mgtr.migrationContext.Log.Infof("T1+T2: renaming %d source table(s) and capturing drain GTID: %s", len(renameClauses), renameAndCaptureQuery) - // @@GLOBAL scope is explicit so the intent is unambiguous in the SQL itself. + // MySQL exposes @@global.gtid_executed, while MariaDB exposes + // @@global.gtid_binlog_pos. // Design: https://github.com/github/gh-ost-tablemove-poc/blob/9dc6df75c4c88ff473906a497836c7518f5614ec/design/coop_cutover.md#32-correctness-verification-for-p4 drainGTIDStr, err := func() (string, error) { rows, err := mgtr.sourcePrimaryDB.QueryContext(cutOverCtx, renameAndCaptureQuery) @@ -1480,14 +1482,14 @@ func (mgtr *Migrator) moveTablesCutOver() (err error) { if err := rows.Err(); err != nil { return "", err } - return "", errors.New("expected result set for @@global.gtid_executed after RENAME") + return "", fmt.Errorf("expected result set for %s after RENAME", drainGTIDVariable) } } if !rows.Next() { if err := rows.Err(); err != nil { return "", err } - return "", errors.New("no row returned for @@global.gtid_executed") + return "", fmt.Errorf("no row returned for %s", drainGTIDVariable) } var gtid string if err := rows.Scan(>id); err != nil { @@ -1547,6 +1549,13 @@ func (mgtr *Migrator) moveTablesCutOver() (err error) { return nil } +func moveTablesDrainGTIDVariable(mysqlVersion string) string { + if mysql.IsMariaDB(mysqlVersion) { + return "@@global.gtid_binlog_pos" + } + return "@@global.gtid_executed" +} + // ExecOnFailureHook executes the onFailure hook, and this method is provided as the only external // hook access point func (mgtr *Migrator) ExecOnFailureHook() (err error) { @@ -2680,15 +2689,18 @@ func (mgtr *Migrator) initiateApplier() error { } } - // ensure performance_schema.metadata_locks is available. - if err := mgtr.applier.StateMetadataLockInstrument(); err != nil { - mgtr.migrationContext.Log.Warning("unable to enable metadata lock instrument, see further error details") - } - if !mgtr.migrationContext.IsOpenMetadataLockInstruments { - if !mgtr.migrationContext.SkipMetadataLockCheck { - return mgtr.migrationContext.Log.Errorf("bailing out because metadata lock instrument not enabled. Use --skip-metadata-lock-check if you wish to proceed without. See https://github.com/github/gh-ost/pull/1536 for details") + if !mgtr.migrationContext.IsMoveTablesMode() { + // Standard cut-over uses the atomic magic-lock protocol and verifies its + // pending metadata lock before releasing the original-table lock. + if err := mgtr.applier.StateMetadataLockInstrument(); err != nil { + mgtr.migrationContext.Log.Warning("unable to enable metadata lock instrument, see further error details") + } + if !mgtr.migrationContext.IsOpenMetadataLockInstruments { + if !mgtr.migrationContext.SkipMetadataLockCheck { + return mgtr.migrationContext.Log.Errorf("bailing out because metadata lock instrument not enabled. Use --skip-metadata-lock-check if you wish to proceed without. See https://github.com/github/gh-ost/pull/1536 for details") + } + mgtr.migrationContext.Log.Warning("proceeding without metadata lock check. There is a small chance of data loss if another session accesses the ghost table during cut-over. See https://github.com/github/gh-ost/pull/1536 for details") } - mgtr.migrationContext.Log.Warning("proceeding without metadata lock check. There is a small chance of data loss if another session accesses the ghost table during cut-over. See https://github.com/github/gh-ost/pull/1536 for details") } if !mgtr.migrationContext.IsMoveTablesMode() { diff --git a/go/logic/migrator_move_tables_cutover_test.go b/go/logic/migrator_move_tables_cutover_test.go index 1f25cddc5..a62cc297b 100644 --- a/go/logic/migrator_move_tables_cutover_test.go +++ b/go/logic/migrator_move_tables_cutover_test.go @@ -84,6 +84,23 @@ func TestMoveTablesCutOver_OnBeforeCutOverHookAbortsBeforeRename(t *testing.T) { "post-state: only the failing T0 hook fires; no OnSuccess, no OnBeginPostponed") } +func TestMoveTablesDrainGTIDVariable(t *testing.T) { + testCases := []struct { + name string + version string + want string + }{ + {name: "MySQL", version: "8.0.41", want: "@@global.gtid_executed"}, + {name: "MariaDB", version: "10.11.18-MariaDB", want: "@@global.gtid_binlog_pos"}, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + require.Equal(t, testCase.want, moveTablesDrainGTIDVariable(testCase.version)) + }) + } +} + type onSuccessCheckHooks struct { *recordingHooks onSuccessCheck func() error From b846243878056fc668c47da420f198467cdea303 Mon Sep 17 00:00:00 2001 From: Eric Yan Date: Tue, 18 Aug 2026 17:08:17 +0000 Subject: [PATCH 2/4] tests: run move-tables suite on MariaDB --- .github/workflows/move-tables-tests.yml | 6 +---- localtests/docker-compose-move-tables.yml | 8 +++---- localtests/move-tables-test.sh | 18 ++++++++++----- script/docker-gh-ost-move-tables-tests | 28 ++++++++++++++++++----- 4 files changed, 39 insertions(+), 21 deletions(-) diff --git a/.github/workflows/move-tables-tests.yml b/.github/workflows/move-tables-tests.yml index e028dbb5a..82e12e731 100644 --- a/.github/workflows/move-tables-tests.yml +++ b/.github/workflows/move-tables-tests.yml @@ -10,11 +10,7 @@ jobs: strategy: fail-fast: false matrix: - image: - # - 'mysql/mysql-server:5.7.41' # metadata locks not supported by default? (https://github.com/github/gh-ost/actions/runs/27841216224/job/82401716601?pr=1714) - - 'mysql:8.0.41' - - 'mysql:8.4.3' - - 'percona/percona-server:8.0.41-32' + image: ['mysql:8.0.41','mysql:8.4.3','percona/percona-server:8.0.41-32','mariadb:10.5.29','mariadb:10.6.27','mariadb:10.11.18','mariadb:11.4.12','mariadb:11.8.8'] env: TEST_MYSQL_IMAGE: ${{ matrix.image }} diff --git a/localtests/docker-compose-move-tables.yml b/localtests/docker-compose-move-tables.yml index 8ba8647ea..29f12fcc1 100644 --- a/localtests/docker-compose-move-tables.yml +++ b/localtests/docker-compose-move-tables.yml @@ -2,7 +2,7 @@ services: mysql-source-primary: image: $TEST_MYSQL_IMAGE container_name: mysql-source-primary - command: --server-id=1 --log-bin=mysql-bin --binlog-format=row --gtid-mode=ON --enforce-gtid-consistency=ON --character-set-server=utf8mb4 $MYSQL_NATIVE_PASSWORD_FLAG + command: --server-id=1 $MYSQL_PRIMARY_OPTIONS environment: MYSQL_ROOT_PASSWORD: opensesame MYSQL_ROOT_HOST: '%' @@ -16,7 +16,7 @@ services: mysql-source-replica: image: $TEST_MYSQL_IMAGE container_name: mysql-source-replica - command: --server-id=2 --log-bin=mysql-bin --binlog-format=row --gtid-mode=ON --enforce-gtid-consistency=ON --log-slave-updates=ON --character-set-server=utf8mb4 $MYSQL_NATIVE_PASSWORD_FLAG + command: --server-id=2 $MYSQL_REPLICA_OPTIONS environment: MYSQL_ROOT_PASSWORD: opensesame MYSQL_ROOT_HOST: '%' @@ -30,7 +30,7 @@ services: mysql-target-primary: image: $TEST_MYSQL_IMAGE container_name: mysql-target-primary - command: --server-id=3 --log-bin=mysql-bin --binlog-format=row --gtid-mode=ON --enforce-gtid-consistency=ON --character-set-server=utf8mb4 $MYSQL_NATIVE_PASSWORD_FLAG + command: --server-id=3 $MYSQL_PRIMARY_OPTIONS environment: MYSQL_ROOT_PASSWORD: opensesame MYSQL_ROOT_HOST: '%' @@ -44,7 +44,7 @@ services: mysql-target-replica: image: $TEST_MYSQL_IMAGE container_name: mysql-target-replica - command: --server-id=4 --log-bin=mysql-bin --binlog-format=row --gtid-mode=ON --enforce-gtid-consistency=ON --log-slave-updates=ON --character-set-server=utf8mb4 $MYSQL_NATIVE_PASSWORD_FLAG + command: --server-id=4 $MYSQL_REPLICA_OPTIONS environment: MYSQL_ROOT_PASSWORD: opensesame MYSQL_ROOT_HOST: '%' diff --git a/localtests/move-tables-test.sh b/localtests/move-tables-test.sh index a3b2ada46..1691c2aba 100755 --- a/localtests/move-tables-test.sh +++ b/localtests/move-tables-test.sh @@ -89,12 +89,18 @@ verify_master_and_replica() { original_sql_mode="$(mysql-exec $cluster primary -e "select @@global.sql_mode" -s -s)" echo "sql_mode on master is ${original_sql_mode}" - current_gtid_mode=$(mysql-exec $cluster primary -s -s -e "select @@global.gtid_mode" 2>/dev/null || echo unsupported) - current_enforce_gtid_consistency=$(mysql-exec $cluster primary -s -s -e "select @@global.enforce_gtid_consistency" 2>/dev/null || echo unsupported) - current_master_server_uuid=$(mysql-exec $cluster primary -s -s -e "select @@global.server_uuid" 2>/dev/null || echo unsupported) - current_replica_server_uuid=$(mysql-exec $cluster replica -s -s -e "select @@global.server_uuid" 2>/dev/null || echo unsupported) - echo "gtid_mode on master is ${current_gtid_mode} with enforce_gtid_consistency=${current_enforce_gtid_consistency}" - echo "server_uuid on master is ${current_master_server_uuid}, replica is ${current_replica_server_uuid}" + mysql_version=$(mysql-exec $cluster primary -s -s -e "select @@version") + if [[ $mysql_version == *MariaDB* ]]; then + current_gtid_mode="ON" + echo "MariaDB GTID replication is enabled" + else + current_gtid_mode=$(mysql-exec $cluster primary -s -s -e "select @@global.gtid_mode" 2>/dev/null || echo unsupported) + current_enforce_gtid_consistency=$(mysql-exec $cluster primary -s -s -e "select @@global.enforce_gtid_consistency" 2>/dev/null || echo unsupported) + current_master_server_uuid=$(mysql-exec $cluster primary -s -s -e "select @@global.server_uuid" 2>/dev/null || echo unsupported) + current_replica_server_uuid=$(mysql-exec $cluster replica -s -s -e "select @@global.server_uuid" 2>/dev/null || echo unsupported) + echo "gtid_mode on master is ${current_gtid_mode} with enforce_gtid_consistency=${current_enforce_gtid_consistency}" + echo "server_uuid on master is ${current_master_server_uuid}, replica is ${current_replica_server_uuid}" + fi echo "Gracefully sleeping for 3 seconds while replica is setting up..." sleep 3 diff --git a/script/docker-gh-ost-move-tables-tests b/script/docker-gh-ost-move-tables-tests index 4e7f09c6a..6ad06b1f7 100755 --- a/script/docker-gh-ost-move-tables-tests +++ b/script/docker-gh-ost-move-tables-tests @@ -54,13 +54,20 @@ setup() { echo "Starting MySQL $TEST_MYSQL_IMAGE containers..." compose_file="$GH_OST_ROOT/localtests/docker-compose-move-tables.yml" - MYSQL_SHA2_RSA_KEYS_FLAG="" + MYSQL_EXTRA_SERVER_OPTIONS="" MYSQL_PASSWORD_HASHING_ALGORITHM="mysql_native_password" if [[ $TEST_MYSQL_IMAGE =~ "mysql:8.4" ]]; then MYSQL_PASSWORD_HASHING_ALGORITHM="caching_sha2_password" - MYSQL_SHA2_RSA_KEYS_FLAG="--caching-sha2-password-auto-generate-rsa-keys=ON" + MYSQL_EXTRA_SERVER_OPTIONS="--caching-sha2-password-auto-generate-rsa-keys=ON" fi - (TEST_MYSQL_IMAGE="$TEST_MYSQL_IMAGE" MYSQL_SHA2_RSA_KEYS_FLAG="$MYSQL_SHA2_RSA_KEYS_FLAG" envsubst <"$compose_file") >"$compose_file.tmp" + if [[ $TEST_MYSQL_IMAGE =~ "mariadb" ]]; then + MYSQL_PRIMARY_OPTIONS="--log-bin=mariadb-bin --binlog-format=row --gtid-strict-mode=ON --character-set-server=utf8mb4" + MYSQL_REPLICA_OPTIONS="$MYSQL_PRIMARY_OPTIONS --log-slave-updates=ON" + else + MYSQL_PRIMARY_OPTIONS="--log-bin=mysql-bin --binlog-format=row --gtid-mode=ON --enforce-gtid-consistency=ON --character-set-server=utf8mb4 $MYSQL_EXTRA_SERVER_OPTIONS" + MYSQL_REPLICA_OPTIONS="$MYSQL_PRIMARY_OPTIONS --log-slave-updates=ON" + fi + (TEST_MYSQL_IMAGE="$TEST_MYSQL_IMAGE" MYSQL_PRIMARY_OPTIONS="$MYSQL_PRIMARY_OPTIONS" MYSQL_REPLICA_OPTIONS="$MYSQL_REPLICA_OPTIONS" envsubst <"$compose_file") >"$compose_file.tmp" docker compose -f "$compose_file.tmp" up -d --wait @@ -74,9 +81,14 @@ setup() { poll_mysql "$cluster" "replica" || exit 1 echo -n "Setting up replication..." - mysql-exec "$cluster" "primary" -e "create user if not exists 'repl'@'%' identified with $MYSQL_PASSWORD_HASHING_ALGORITHM by 'repl';" + if [[ $TEST_MYSQL_IMAGE =~ "mariadb" ]]; then + mysql-exec "$cluster" "primary" -e "create user if not exists 'repl'@'%' identified by 'repl';" + mysql-exec "$cluster" "primary" -e "create user if not exists 'gh-ost'@'%' identified by 'gh-ost';" + else + mysql-exec "$cluster" "primary" -e "create user if not exists 'repl'@'%' identified with $MYSQL_PASSWORD_HASHING_ALGORITHM by 'repl';" + mysql-exec "$cluster" "primary" -e "create user if not exists 'gh-ost'@'%' identified with $MYSQL_PASSWORD_HASHING_ALGORITHM by 'gh-ost';" + fi mysql-exec "$cluster" "primary" -e "grant replication slave on *.* to 'repl'@'%'; flush privileges;" - mysql-exec "$cluster" "primary" -e "create user if not exists 'gh-ost'@'%' identified with $MYSQL_PASSWORD_HASHING_ALGORITHM by 'gh-ost';" mysql-exec "$cluster" "primary" -e "grant all on *.* to 'gh-ost'@'%';" primary_port=3307 @@ -85,7 +97,11 @@ setup() { fi sleep 1 - if [[ $TEST_MYSQL_IMAGE =~ "mysql:8.4" ]]; then + if [[ $TEST_MYSQL_IMAGE =~ "mariadb" ]]; then + mysql-exec "$cluster" "replica" -e "reset master; set global gtid_slave_pos=''; set global gtid_domain_id=1;" + mysql-exec "$cluster" "replica" -e "change master to master_host='mysql-$cluster-primary', master_port=$primary_port, master_user='repl', master_password='repl', master_use_gtid=slave_pos;" + mysql-exec "$cluster" "replica" -e "start slave;" + elif [[ $TEST_MYSQL_IMAGE =~ "mysql:8.4" ]]; then mysql-exec "$cluster" "replica" -e "change replication source to source_host='mysql-$cluster-primary', source_port=$primary_port, source_user='repl', source_password='repl', source_auto_position=1, source_ssl=1;" mysql-exec "$cluster" "replica" -e "start replica;" else From f6cfc21fd456897d58a2b475b3220cd64de37690 Mon Sep 17 00:00:00 2001 From: Eric Yan Date: Tue, 18 Aug 2026 17:08:23 +0000 Subject: [PATCH 3/4] tests: make move-tables generated columns fixture portable --- localtests/move-tables/generated-columns/create.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/localtests/move-tables/generated-columns/create.sql b/localtests/move-tables/generated-columns/create.sql index c326893c3..aa240aced 100644 --- a/localtests/move-tables/generated-columns/create.sql +++ b/localtests/move-tables/generated-columns/create.sql @@ -2,9 +2,9 @@ drop table if exists gh_ost_test; create table gh_ost_test ( id int auto_increment, a int not null, - virtual_sum int as (a + 10) virtual not null, + virtual_sum int as (a + 10) virtual, b int not null, - stored_sum int as (a + b) stored not null, + stored_sum int as (a + b) stored, json_value json default null, virtual_json_value varchar(16) as ( coalesce(json_unquote(json_extract(json_value, '$.value')), 'direct') From 405dffe72e6af0a5cc197203a0ad6f854bae01a6 Mon Sep 17 00:00:00 2001 From: Eric Yan Date: Wed, 19 Aug 2026 11:41:38 +0000 Subject: [PATCH 4/4] tests: configure MariaDB in manual move-tables setup --- script/move-tables/setup | 89 +++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 42 deletions(-) diff --git a/script/move-tables/setup b/script/move-tables/setup index df7644c3c..984d1264c 100755 --- a/script/move-tables/setup +++ b/script/move-tables/setup @@ -86,6 +86,41 @@ ensure_host_aliases() { fi } +setup_cluster_replication() { + local cluster="$1" + local primary_port="$2" + local primary_exec="exec-mysql-${cluster}-primary" + local replica_exec="exec-mysql-${cluster}-replica" + + echo -n "Setting up replication for $cluster cluster..." + if [[ $TEST_MYSQL_IMAGE =~ "mariadb" ]]; then + "$primary_exec" -e "create user if not exists 'repl'@'%' identified by 'repl';" + "$primary_exec" -e "create user if not exists 'gh-ost'@'%' identified by 'gh-ost';" + else + "$primary_exec" -e "create user if not exists 'repl'@'%' identified with $MYSQL_PASSWORD_HASHING_ALGORITHM by 'repl';" + "$primary_exec" -e "create user if not exists 'gh-ost'@'%' identified with $MYSQL_PASSWORD_HASHING_ALGORITHM by 'gh-ost';" + fi + "$primary_exec" -e "grant replication slave on *.* to 'repl'@'%'; flush privileges;" + "$primary_exec" -e "grant all on *.* to 'gh-ost'@'%';" + + sleep 1 + if [[ $TEST_MYSQL_IMAGE =~ "mariadb" ]]; then + "$replica_exec" -e "reset master; set global gtid_slave_pos=''; set global gtid_domain_id=1;" + "$replica_exec" -e "change master to master_host='mysql-${cluster}-primary', master_port=$primary_port, master_user='repl', master_password='repl', master_use_gtid=slave_pos;" + "$replica_exec" -e "start slave;" + "$replica_exec" -e "set global read_only=ON;" + elif [[ $TEST_MYSQL_IMAGE =~ "mysql:8.4" ]]; then + "$replica_exec" -e "change replication source to source_host='mysql-${cluster}-primary', source_port=$primary_port, source_user='repl', source_password='repl', source_auto_position=1, source_ssl=1;" + "$replica_exec" -e "start replica;" + "$replica_exec" -e "set global read_only=ON; set global super_read_only=ON;" + else + "$replica_exec" -e "change master to master_host='mysql-${cluster}-primary', master_port=$primary_port, master_user='repl', master_password='repl', master_auto_position=1;" + "$replica_exec" -e "start slave;" + "$replica_exec" -e "set global read_only=ON; set global super_read_only=ON;" + fi + echo "OK" +} + setup() { [ -z "$TEST_MYSQL_IMAGE" ] && TEST_MYSQL_IMAGE="mysql:8.0.41" @@ -93,15 +128,20 @@ setup() { echo "Starting MySQL $TEST_MYSQL_IMAGE containers (2 clusters)..." compose_file="$GH_OST_ROOT/localtests/docker-compose-move-tables.yml" - MYSQL_SHA2_RSA_KEYS_FLAG="" + MYSQL_EXTRA_SERVER_OPTIONS="" MYSQL_PASSWORD_HASHING_ALGORITHM="mysql_native_password" - MYSQL_NATIVE_PASSWORD_FLAG="" if [[ $TEST_MYSQL_IMAGE =~ "mysql:8.4" ]]; then MYSQL_PASSWORD_HASHING_ALGORITHM="caching_sha2_password" - MYSQL_SHA2_RSA_KEYS_FLAG="--caching-sha2-password-auto-generate-rsa-keys=ON" - MYSQL_NATIVE_PASSWORD_FLAG="$MYSQL_SHA2_RSA_KEYS_FLAG" + MYSQL_EXTRA_SERVER_OPTIONS="--caching-sha2-password-auto-generate-rsa-keys=ON" fi - (TEST_MYSQL_IMAGE="$TEST_MYSQL_IMAGE" MYSQL_NATIVE_PASSWORD_FLAG="$MYSQL_NATIVE_PASSWORD_FLAG" envsubst <"$compose_file") >"$compose_file.tmp" + if [[ $TEST_MYSQL_IMAGE =~ "mariadb" ]]; then + MYSQL_PRIMARY_OPTIONS="--log-bin=mariadb-bin --binlog-format=row --gtid-strict-mode=ON --character-set-server=utf8mb4" + MYSQL_REPLICA_OPTIONS="$MYSQL_PRIMARY_OPTIONS --log-slave-updates=ON" + else + MYSQL_PRIMARY_OPTIONS="--log-bin=mysql-bin --binlog-format=row --gtid-mode=ON --enforce-gtid-consistency=ON --character-set-server=utf8mb4 $MYSQL_EXTRA_SERVER_OPTIONS" + MYSQL_REPLICA_OPTIONS="$MYSQL_PRIMARY_OPTIONS --log-slave-updates=ON" + fi + (TEST_MYSQL_IMAGE="$TEST_MYSQL_IMAGE" MYSQL_PRIMARY_OPTIONS="$MYSQL_PRIMARY_OPTIONS" MYSQL_REPLICA_OPTIONS="$MYSQL_REPLICA_OPTIONS" envsubst <"$compose_file") >"$compose_file.tmp" docker compose -f "$compose_file.tmp" up -d --wait @@ -111,43 +151,8 @@ setup() { poll_mysql "target-primary" || exit 1 poll_mysql "target-replica" || exit 1 - # Setup replication for source cluster, not idempotent - echo -n "Setting up replication for source cluster..." - exec-mysql-source-primary -e "create user if not exists 'repl'@'%' identified with $MYSQL_PASSWORD_HASHING_ALGORITHM by 'repl';" - exec-mysql-source-primary -e "grant replication slave on *.* to 'repl'@'%'; flush privileges;" - exec-mysql-source-primary -e "create user if not exists 'gh-ost'@'%' identified with $MYSQL_PASSWORD_HASHING_ALGORITHM by 'gh-ost';" - exec-mysql-source-primary -e "grant all on *.* to 'gh-ost'@'%';" - - sleep 1 - if [[ $TEST_MYSQL_IMAGE =~ "mysql:8.4" ]]; then - exec-mysql-source-replica -e "change replication source to source_host='mysql-source-primary', source_port=3307, source_user='repl', source_password='repl', source_auto_position=1, source_ssl=1;" - exec-mysql-source-replica -e "start replica;" - else - exec-mysql-source-replica -e "change master to master_host='mysql-source-primary', master_port=3307, master_user='repl', master_password='repl', master_auto_position=1;" - exec-mysql-source-replica -e "start slave;" - fi - # Keep replicas non-writable so local move-tables runs that point --host at - # a replica fail at cutover (RENAME) instead of mutating only the replica. - exec-mysql-source-replica -e "set global read_only=ON; set global super_read_only=ON;" - echo "OK" - - # Setup replication for target cluster - echo -n "Setting up replication for target cluster..." - exec-mysql-target-primary -e "create user if not exists 'repl'@'%' identified with $MYSQL_PASSWORD_HASHING_ALGORITHM by 'repl';" - exec-mysql-target-primary -e "grant replication slave on *.* to 'repl'@'%'; flush privileges;" - exec-mysql-target-primary -e "create user if not exists 'gh-ost'@'%' identified with $MYSQL_PASSWORD_HASHING_ALGORITHM by 'gh-ost';" - exec-mysql-target-primary -e "grant all on *.* to 'gh-ost'@'%';" - - sleep 1 - if [[ $TEST_MYSQL_IMAGE =~ "mysql:8.4" ]]; then - exec-mysql-target-replica -e "change replication source to source_host='mysql-target-primary', source_port=3309, source_user='repl', source_password='repl', source_auto_position=1, source_ssl=1;" - exec-mysql-target-replica -e "start replica;" - else - exec-mysql-target-replica -e "change master to master_host='mysql-target-primary', master_port=3309, master_user='repl', master_password='repl', master_auto_position=1;" - exec-mysql-target-replica -e "start slave;" - fi - exec-mysql-target-replica -e "set global read_only=ON; set global super_read_only=ON;" - echo "OK" + setup_cluster_replication "source" 3307 + setup_cluster_replication "target" 3309 echo -n "Initializing '$DATABASE_NAME' database into each cluster..." exec-mysql-source-primary -e "CREATE DATABASE IF NOT EXISTS $DATABASE_NAME;"