From 8922f644cfa1043aff6ff70a3e61127f0d2b5de1 Mon Sep 17 00:00:00 2001 From: dbking Date: Mon, 21 Apr 2025 12:06:47 +0800 Subject: [PATCH 1/8] Before the successful renaming, a session accessed the ghost table, which had already unlocked the original table. There is a very small probability that other sessions dml operations on the original table will occur, and this dml operation will appear in the original table after renaming, resulting in data loss. --- go/logic/applier.go | 43 ++++++++++++++++++++++++++++++++++++++++++- go/logic/migrator.go | 4 +++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/go/logic/applier.go b/go/logic/applier.go index 0491aae8d..d6f2a2c7a 100644 --- a/go/logic/applier.go +++ b/go/logic/applier.go @@ -1099,7 +1099,7 @@ func (this *Applier) RevertAtomicCutOverWaitTimeout() { } // AtomicCutOverMagicLock -func (this *Applier) AtomicCutOverMagicLock(sessionIdChan chan int64, tableLocked chan<- error, okToUnlockTable <-chan bool, tableUnlocked chan<- error) error { +func (this *Applier) AtomicCutOverMagicLock(sessionIdChan chan int64, tableLocked chan<- error, okToUnlockTable <-chan bool, tableUnlocked chan<- error, renameLockSessionId *int64) error { tx, err := this.db.Begin() if err != nil { tableLocked <- err @@ -1190,6 +1190,23 @@ func (this *Applier) AtomicCutOverMagicLock(sessionIdChan chan int64, tableLocke // We DO NOT return here because we must `UNLOCK TABLES`! } + this.migrationContext.Log.Infof("Session renameLockSessionId is %+v", *renameLockSessionId) + // checking the lock holded by rename session + if *renameLockSessionId > 0 { + for i := 0; i <= 50; i++ { + err := this.ExpectMetadataLock(*renameLockSessionId) + if err == nil { + this.migrationContext.Log.Infof("Rename session is pending lock on the origin table !") + break + } else { + if i == 50 { + return err + } + time.Sleep(10 * time.Millisecond) + } + } + } + // Tables still locked this.migrationContext.Log.Infof("Releasing lock from %s.%s, %s.%s", sql.EscapeName(this.migrationContext.DatabaseName), @@ -1409,3 +1426,27 @@ func (this *Applier) Teardown() { this.singletonDB.Close() atomic.StoreInt64(&this.finishedMigrating, 1) } + +func (this *Applier) ExpectMetadataLock(sessionId int64) error { + found := false + query := ` + select /* gh-ost */ m.owner_thread_id + from performance_schema.metadata_locks m join performance_schema.threads t + on m.owner_thread_id=t.thread_id + where m.object_type = 'TABLE' and m.object_schema = ? and m.object_name = ? + and m.lock_type = 'EXCLUSIVE' and m.lock_status = 'PENDING' + and t.processlist_id = ? + ` + err := sqlutils.QueryRowsMap(this.db, query, func(m sqlutils.RowMap) error { + found = true + return nil + }, this.migrationContext.DatabaseName, this.migrationContext.OriginalTableName, sessionId) + if err != nil { + return err + } + if !found { + err = fmt.Errorf("cannot find PENDING metadata lock on original table: `%s`.`%s`", this.migrationContext.DatabaseName, this.migrationContext.OriginalTableName) + return this.migrationContext.Log.Errore(err) + } + return nil +} diff --git a/go/logic/migrator.go b/go/logic/migrator.go index 09de0977b..58308d9ad 100644 --- a/go/logic/migrator.go +++ b/go/logic/migrator.go @@ -669,8 +669,9 @@ func (this *Migrator) atomicCutOver() (err error) { lockOriginalSessionIdChan := make(chan int64, 2) tableLocked := make(chan error, 2) tableUnlocked := make(chan error, 2) + var renameLockSessionId int64 go func() { - if err := this.applier.AtomicCutOverMagicLock(lockOriginalSessionIdChan, tableLocked, okToUnlockTable, tableUnlocked); err != nil { + if err := this.applier.AtomicCutOverMagicLock(lockOriginalSessionIdChan, tableLocked, okToUnlockTable, tableUnlocked, &renameLockSessionId); err != nil { this.migrationContext.Log.Errore(err) } }() @@ -735,6 +736,7 @@ func (this *Migrator) atomicCutOver() (err error) { // Now that we've found the RENAME blocking, AND the locking connection still alive, // we know it is safe to proceed to release the lock + renameLockSessionId = renameSessionId okToUnlockTable <- true // BAM! magic table dropped, original table lock is released // -> RENAME released -> queries on original are unblocked. From 807b6706c6e2df4d1605389dcc510b377656b138 Mon Sep 17 00:00:00 2001 From: dbking Date: Tue, 22 Apr 2025 10:38:41 +0800 Subject: [PATCH 2/8] fix: fmt --- go/logic/migrator.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go/logic/migrator.go b/go/logic/migrator.go index 58308d9ad..4ffac3532 100644 --- a/go/logic/migrator.go +++ b/go/logic/migrator.go @@ -669,7 +669,7 @@ func (this *Migrator) atomicCutOver() (err error) { lockOriginalSessionIdChan := make(chan int64, 2) tableLocked := make(chan error, 2) tableUnlocked := make(chan error, 2) - var renameLockSessionId int64 + var renameLockSessionId int64 go func() { if err := this.applier.AtomicCutOverMagicLock(lockOriginalSessionIdChan, tableLocked, okToUnlockTable, tableUnlocked, &renameLockSessionId); err != nil { this.migrationContext.Log.Errore(err) @@ -736,7 +736,7 @@ func (this *Migrator) atomicCutOver() (err error) { // Now that we've found the RENAME blocking, AND the locking connection still alive, // we know it is safe to proceed to release the lock - renameLockSessionId = renameSessionId + renameLockSessionId = renameSessionId okToUnlockTable <- true // BAM! magic table dropped, original table lock is released // -> RENAME released -> queries on original are unblocked. From f6b68dc7c51d068754674430eaecade442e546d6 Mon Sep 17 00:00:00 2001 From: dbking Date: Wed, 30 Apr 2025 09:36:07 +0800 Subject: [PATCH 3/8] fix: Before the successful renaming, a session held a ghost lock, resulting in data loss --- go/logic/applier.go | 9 ++-- go/logic/migrator_test.go | 86 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 6 deletions(-) diff --git a/go/logic/applier.go b/go/logic/applier.go index d6f2a2c7a..89fcad098 100644 --- a/go/logic/applier.go +++ b/go/logic/applier.go @@ -1193,20 +1193,17 @@ func (this *Applier) AtomicCutOverMagicLock(sessionIdChan chan int64, tableLocke this.migrationContext.Log.Infof("Session renameLockSessionId is %+v", *renameLockSessionId) // checking the lock holded by rename session if *renameLockSessionId > 0 { - for i := 0; i <= 50; i++ { + sleepDuration := time.Duration(10*this.migrationContext.CutOverLockTimeoutSeconds) * time.Millisecond + for i := 1; i <= 100; i++ { err := this.ExpectMetadataLock(*renameLockSessionId) if err == nil { this.migrationContext.Log.Infof("Rename session is pending lock on the origin table !") break } else { - if i == 50 { - return err - } - time.Sleep(10 * time.Millisecond) + time.Sleep(sleepDuration) } } } - // Tables still locked this.migrationContext.Log.Infof("Releasing lock from %s.%s, %s.%s", sql.EscapeName(this.migrationContext.DatabaseName), diff --git a/go/logic/migrator_test.go b/go/logic/migrator_test.go index 813909208..7409e44e6 100644 --- a/go/logic/migrator_test.go +++ b/go/logic/migrator_test.go @@ -9,6 +9,7 @@ import ( "context" gosql "database/sql" "errors" + "fmt" "os" "path/filepath" "runtime" @@ -445,6 +446,91 @@ func TestMigratorRetryWithExponentialBackoff(t *testing.T) { assert.Equal(t, tries, 100) } +func (suite *MigratorTestSuite) TestCutOverLossDataCaseLockGhostBeforeRename() { + ctx := context.Background() + + _, err := suite.db.ExecContext(ctx, "CREATE TABLE test.testing (id INT PRIMARY KEY, name VARCHAR(64))") + suite.Require().NoError(err) + + _, err = suite.db.ExecContext(ctx, "insert into test.testing values(1,'a')") + suite.Require().NoError(err) + + done := make(chan error, 1) + go func() { + connectionConfig, err := GetConnectionConfig(ctx, suite.mysqlContainer) + if err != nil { + done <- err + return + } + migrationContext := base.NewMigrationContext() + migrationContext.AllowedRunningOnMaster = true + migrationContext.ApplierConnectionConfig = connectionConfig + migrationContext.InspectorConnectionConfig = connectionConfig + migrationContext.DatabaseName = "test" + migrationContext.SkipPortValidation = true + migrationContext.OriginalTableName = "testing" + migrationContext.SetConnectionConfig("innodb") + migrationContext.AlterStatementOptions = "ADD COLUMN foobar varchar(255)" + migrationContext.ReplicaServerId = 99999 + migrationContext.HeartbeatIntervalMilliseconds = 100 + migrationContext.ThrottleHTTPIntervalMillis = 100 + migrationContext.ThrottleHTTPTimeoutMillis = 1000 + migrationContext.CutOverLockTimeoutSeconds = 4 + + //nolint:dogsled + _, filename, _, _ := runtime.Caller(0) + migrationContext.ServeSocketFile = filepath.Join(filepath.Dir(filename), "../../tmp/gh-ost.sock") + migrationContext.PostponeCutOverFlagFile = filepath.Join(filepath.Dir(filename), "../../tmp/ghost.postpone.flag") + + migrator := NewMigrator(migrationContext, "0.0.0") + + done <- migrator.Migrate() + }() + + time.Sleep(2 * time.Second) + _, filename, _, _ := runtime.Caller(0) + err = os.Remove(filepath.Join(filepath.Dir(filename), "../../tmp/ghost.postpone.flag")) + if err != nil { + suite.Require().NoError(err) + } + time.Sleep(1 * time.Second) + go func() { + holdConn, err := suite.db.Conn(ctx) + suite.Require().NoError(err) + _, err = holdConn.ExecContext(ctx, "SELECT *, sleep(5) FROM test._testing_gho WHERE id = 1") + suite.Require().NoError(err) + }() + + dmlConn, err := suite.db.Conn(ctx) + suite.Require().NoError(err) + + _, err = dmlConn.ExecContext(ctx, "insert into test.testing(id, name) values(2,'b')") + fmt.Println("insert into table original table") + suite.Require().NoError(err) + + migrateErr := <-done + suite.Require().NoError(migrateErr) + + // Verify the new column was added + var delValue, OriginalValue int64 + err = suite.db.QueryRow("select count(*) from test._testing_del").Scan(&delValue) + suite.Require().NoError(err) + + err = suite.db.QueryRow("select count(*) from test.testing").Scan(&OriginalValue) + suite.Require().NoError(err) + + suite.Require().LessOrEqual(delValue, delValue) + + var tableName, createTableSQL string + //nolint:execinquery + err = suite.db.QueryRow("SHOW CREATE TABLE test.testing").Scan(&tableName, &createTableSQL) + suite.Require().NoError(err) + + suite.Require().Equal("testing", tableName) + suite.Require().Equal("CREATE TABLE `testing` (\n `id` int NOT NULL,\n `name` varchar(64) DEFAULT NULL,\n `foobar` varchar(255) DEFAULT NULL,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", createTableSQL) + +} + func TestMigrator(t *testing.T) { suite.Run(t, new(MigratorTestSuite)) } From feff63fcdb434e73bf22d8fd4e2a3feda99c4dcc Mon Sep 17 00:00:00 2001 From: dbking Date: Wed, 30 Apr 2025 10:05:04 +0800 Subject: [PATCH 4/8] fix: Adjust the lock time of Ghost --- go/logic/migrator_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/logic/migrator_test.go b/go/logic/migrator_test.go index 7409e44e6..5ef20ba5b 100644 --- a/go/logic/migrator_test.go +++ b/go/logic/migrator_test.go @@ -497,7 +497,7 @@ func (suite *MigratorTestSuite) TestCutOverLossDataCaseLockGhostBeforeRename() { go func() { holdConn, err := suite.db.Conn(ctx) suite.Require().NoError(err) - _, err = holdConn.ExecContext(ctx, "SELECT *, sleep(5) FROM test._testing_gho WHERE id = 1") + _, err = holdConn.ExecContext(ctx, "SELECT *, sleep(2) FROM test._testing_gho WHERE id = 1") suite.Require().NoError(err) }() From 1794e901b84a4eda3332b7d96104332dbe086303 Mon Sep 17 00:00:00 2001 From: dbking Date: Wed, 30 Apr 2025 10:08:12 +0800 Subject: [PATCH 5/8] fix: Fix the comparison error between two tables --- go/logic/migrator_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/logic/migrator_test.go b/go/logic/migrator_test.go index 5ef20ba5b..27c10bf5c 100644 --- a/go/logic/migrator_test.go +++ b/go/logic/migrator_test.go @@ -519,7 +519,7 @@ func (suite *MigratorTestSuite) TestCutOverLossDataCaseLockGhostBeforeRename() { err = suite.db.QueryRow("select count(*) from test.testing").Scan(&OriginalValue) suite.Require().NoError(err) - suite.Require().LessOrEqual(delValue, delValue) + suite.Require().LessOrEqual(delValue, OriginalValue) var tableName, createTableSQL string //nolint:execinquery From de1ced391261c9229684c6963684ec8c0b185686 Mon Sep 17 00:00:00 2001 From: dbking Date: Mon, 12 May 2025 10:59:52 +0800 Subject: [PATCH 6/8] fix: add AllowSetupMetadataLockInstruments Compatible with MySQL 5.7 and previous versions --- go/base/context.go | 4 +++- go/cmd/gh-ost/main.go | 1 + go/logic/applier.go | 24 +++++++++++++++++++++++- go/logic/migrator.go | 4 ++++ go/logic/migrator_test.go | 4 ++-- 5 files changed, 33 insertions(+), 4 deletions(-) diff --git a/go/base/context.go b/go/base/context.go index ac077076f..e4008cdd6 100644 --- a/go/base/context.go +++ b/go/base/context.go @@ -247,7 +247,9 @@ type MigrationContext struct { recentBinlogCoordinates mysql.BinlogCoordinates - BinlogSyncerMaxReconnectAttempts int + BinlogSyncerMaxReconnectAttempts int + AllowSetupMetadataLockInstruments bool + IsOpenMetadataLockInstruments bool Log Logger } diff --git a/go/cmd/gh-ost/main.go b/go/cmd/gh-ost/main.go index a1670cdd4..4a28ad1aa 100644 --- a/go/cmd/gh-ost/main.go +++ b/go/cmd/gh-ost/main.go @@ -137,6 +137,7 @@ func main() { flag.Int64Var(&migrationContext.HooksStatusIntervalSec, "hooks-status-interval", 60, "how many seconds to wait between calling onStatus hook") flag.UintVar(&migrationContext.ReplicaServerId, "replica-server-id", 99999, "server id used by gh-ost process. Default: 99999") + flag.BoolVar(&migrationContext.AllowSetupMetadataLockInstruments, "allow-setup-metadata-lock-instruments", false, "validate rename session hold the MDL of original table before unlock tables in cut-over phase") flag.IntVar(&migrationContext.BinlogSyncerMaxReconnectAttempts, "binlogsyncer-max-reconnect-attempts", 0, "when master node fails, the maximum number of binlog synchronization attempts to reconnect. 0 is unlimited") flag.BoolVar(&migrationContext.IncludeTriggers, "include-triggers", false, "When true, the triggers (if exist) will be created on the new table") diff --git a/go/logic/applier.go b/go/logic/applier.go index 89fcad098..6770a961b 100644 --- a/go/logic/applier.go +++ b/go/logic/applier.go @@ -416,6 +416,28 @@ func (this *Applier) dropTable(tableName string) error { return nil } +func (this *Applier) StateMetadataLockInstrument() error { + query := `select /*+ MAX_EXECUTION_TIME(300) */ ENABLED, TIMED from performance_schema.setup_instruments WHERE NAME = 'wait/lock/metadata/sql/mdl'` + var enabled, timed string + if err := this.db.QueryRow(query).Scan(&enabled, &timed); err != nil { + return this.migrationContext.Log.Errorf("query performance_schema.setup_instruments with name wait/lock/metadata/sql/mdl error: %s", err) + } + if strings.EqualFold(enabled, "YES") && strings.EqualFold(timed, "YES") { + this.migrationContext.IsOpenMetadataLockInstruments = true + return nil + } + if !this.migrationContext.AllowSetupMetadataLockInstruments { + return nil + } + this.migrationContext.Log.Infof("instrument wait/lock/metadata/sql/mdl state: enabled %s, timed %s", enabled, timed) + if _, err := this.db.Exec(`UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' WHERE NAME = 'wait/lock/metadata/sql/mdl'`); err != nil { + return this.migrationContext.Log.Errorf("enable instrument wait/lock/metadata/sql/mdl error: %s", err) + } + this.migrationContext.IsOpenMetadataLockInstruments = true + this.migrationContext.Log.Infof("instrument wait/lock/metadata/sql/mdl enabled") + return nil +} + // dropTriggers drop the triggers on the applied host func (this *Applier) DropTriggersFromGhost() error { if len(this.migrationContext.Triggers) > 0 { @@ -1192,7 +1214,7 @@ func (this *Applier) AtomicCutOverMagicLock(sessionIdChan chan int64, tableLocke this.migrationContext.Log.Infof("Session renameLockSessionId is %+v", *renameLockSessionId) // checking the lock holded by rename session - if *renameLockSessionId > 0 { + if *renameLockSessionId > 0 && this.migrationContext.IsOpenMetadataLockInstruments { sleepDuration := time.Duration(10*this.migrationContext.CutOverLockTimeoutSeconds) * time.Millisecond for i := 1; i <= 100; i++ { err := this.ExpectMetadataLock(*renameLockSessionId) diff --git a/go/logic/migrator.go b/go/logic/migrator.go index 4ffac3532..d532e79f5 100644 --- a/go/logic/migrator.go +++ b/go/logic/migrator.go @@ -1205,6 +1205,10 @@ func (this *Migrator) initiateApplier() error { } } this.applier.WriteChangelogState(string(GhostTableMigrated)) + if err := this.applier.StateMetadataLockInstrument(); err != nil { + this.migrationContext.Log.Errorf("Unable to enable metadata lock instrument, see further error details. Bailing out") + return err + } go this.applier.InitiateHeartbeat() return nil } diff --git a/go/logic/migrator_test.go b/go/logic/migrator_test.go index 27c10bf5c..b94c81c4c 100644 --- a/go/logic/migrator_test.go +++ b/go/logic/migrator_test.go @@ -477,17 +477,18 @@ func (suite *MigratorTestSuite) TestCutOverLossDataCaseLockGhostBeforeRename() { migrationContext.ThrottleHTTPTimeoutMillis = 1000 migrationContext.CutOverLockTimeoutSeconds = 4 - //nolint:dogsled _, filename, _, _ := runtime.Caller(0) migrationContext.ServeSocketFile = filepath.Join(filepath.Dir(filename), "../../tmp/gh-ost.sock") migrationContext.PostponeCutOverFlagFile = filepath.Join(filepath.Dir(filename), "../../tmp/ghost.postpone.flag") migrator := NewMigrator(migrationContext, "0.0.0") + //nolint:contextcheck done <- migrator.Migrate() }() time.Sleep(2 * time.Second) + //nolint:dogsled _, filename, _, _ := runtime.Caller(0) err = os.Remove(filepath.Join(filepath.Dir(filename), "../../tmp/ghost.postpone.flag")) if err != nil { @@ -528,7 +529,6 @@ func (suite *MigratorTestSuite) TestCutOverLossDataCaseLockGhostBeforeRename() { suite.Require().Equal("testing", tableName) suite.Require().Equal("CREATE TABLE `testing` (\n `id` int NOT NULL,\n `name` varchar(64) DEFAULT NULL,\n `foobar` varchar(255) DEFAULT NULL,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", createTableSQL) - } func TestMigrator(t *testing.T) { From e7a2766e0e8de9afea0dfda7601c96ebe3538f59 Mon Sep 17 00:00:00 2001 From: meiji163 Date: Fri, 27 Jun 2025 10:56:13 -0700 Subject: [PATCH 7/8] fix rename lock test --- go/logic/applier.go | 2 +- go/logic/migrator_test.go | 31 +++++++++++++------------------ 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/go/logic/applier.go b/go/logic/applier.go index 74253f4a5..a7edaed24 100644 --- a/go/logic/applier.go +++ b/go/logic/applier.go @@ -1209,7 +1209,7 @@ func (this *Applier) AtomicCutOverMagicLock(sessionIdChan chan int64, tableLocke } this.migrationContext.Log.Infof("Session renameLockSessionId is %+v", *renameLockSessionId) - // checking the lock holded by rename session + // Checking the lock is held by rename session if *renameLockSessionId > 0 && this.migrationContext.IsOpenMetadataLockInstruments { sleepDuration := time.Duration(10*this.migrationContext.CutOverLockTimeoutSeconds) * time.Millisecond for i := 1; i <= 100; i++ { diff --git a/go/logic/migrator_test.go b/go/logic/migrator_test.go index 5d9b08652..88c7b4323 100644 --- a/go/logic/migrator_test.go +++ b/go/logic/migrator_test.go @@ -23,7 +23,7 @@ import ( "github.com/stretchr/testify/suite" "github.com/testcontainers/testcontainers-go/modules/mysql" - "fmt" + "runtime" "github.com/github/gh-ost/go/base" "github.com/github/gh-ost/go/binlog" @@ -577,36 +577,29 @@ func TestMigratorRetryWithExponentialBackoff(t *testing.T) { func (suite *MigratorTestSuite) TestCutOverLossDataCaseLockGhostBeforeRename() { ctx := context.Background() - _, err := suite.db.ExecContext(ctx, "CREATE TABLE test.testing (id INT PRIMARY KEY, name VARCHAR(64))") + _, err := suite.db.ExecContext(ctx, fmt.Sprintf("CREATE TABLE %s (id INT PRIMARY KEY, name VARCHAR(64))", getTestTableName())) suite.Require().NoError(err) - _, err = suite.db.ExecContext(ctx, "insert into test.testing values(1,'a')") + _, err = suite.db.ExecContext(ctx, fmt.Sprintf("insert into %s values(1,'a')", getTestTableName())) suite.Require().NoError(err) done := make(chan error, 1) go func() { - connectionConfig, err := GetConnectionConfig(ctx, suite.mysqlContainer) + connectionConfig, err := getTestConnectionConfig(ctx, suite.mysqlContainer) if err != nil { done <- err return } - migrationContext := base.NewMigrationContext() - migrationContext.AllowedRunningOnMaster = true + migrationContext := newTestMigrationContext() migrationContext.ApplierConnectionConfig = connectionConfig migrationContext.InspectorConnectionConfig = connectionConfig - migrationContext.DatabaseName = "test" - migrationContext.SkipPortValidation = true - migrationContext.OriginalTableName = "testing" migrationContext.SetConnectionConfig("innodb") + migrationContext.AllowSetupMetadataLockInstruments = true migrationContext.AlterStatementOptions = "ADD COLUMN foobar varchar(255)" - migrationContext.ReplicaServerId = 99999 migrationContext.HeartbeatIntervalMilliseconds = 100 - migrationContext.ThrottleHTTPIntervalMillis = 100 - migrationContext.ThrottleHTTPTimeoutMillis = 1000 migrationContext.CutOverLockTimeoutSeconds = 4 _, filename, _, _ := runtime.Caller(0) - migrationContext.ServeSocketFile = filepath.Join(filepath.Dir(filename), "../../tmp/gh-ost.sock") migrationContext.PostponeCutOverFlagFile = filepath.Join(filepath.Dir(filename), "../../tmp/ghost.postpone.flag") migrator := NewMigrator(migrationContext, "0.0.0") @@ -633,7 +626,7 @@ func (suite *MigratorTestSuite) TestCutOverLossDataCaseLockGhostBeforeRename() { dmlConn, err := suite.db.Conn(ctx) suite.Require().NoError(err) - _, err = dmlConn.ExecContext(ctx, "insert into test.testing(id, name) values(2,'b')") + _, err = dmlConn.ExecContext(ctx, fmt.Sprintf("insert into %s (id, name) values(2,'b')", getTestTableName())) fmt.Println("insert into table original table") suite.Require().NoError(err) @@ -642,20 +635,22 @@ func (suite *MigratorTestSuite) TestCutOverLossDataCaseLockGhostBeforeRename() { // Verify the new column was added var delValue, OriginalValue int64 - err = suite.db.QueryRow("select count(*) from test._testing_del").Scan(&delValue) + err = suite.db.QueryRow( + fmt.Sprintf("select count(*) from %s._%s_del", testMysqlDatabase, testMysqlTableName), + ).Scan(&delValue) suite.Require().NoError(err) - err = suite.db.QueryRow("select count(*) from test.testing").Scan(&OriginalValue) + err = suite.db.QueryRow("select count(*) from " + getTestTableName()).Scan(&OriginalValue) suite.Require().NoError(err) suite.Require().LessOrEqual(delValue, OriginalValue) var tableName, createTableSQL string //nolint:execinquery - err = suite.db.QueryRow("SHOW CREATE TABLE test.testing").Scan(&tableName, &createTableSQL) + err = suite.db.QueryRow("SHOW CREATE TABLE "+getTestTableName()).Scan(&tableName, &createTableSQL) suite.Require().NoError(err) - suite.Require().Equal("testing", tableName) + suite.Require().Equal(testMysqlTableName, tableName) suite.Require().Equal("CREATE TABLE `testing` (\n `id` int NOT NULL,\n `name` varchar(64) DEFAULT NULL,\n `foobar` varchar(255) DEFAULT NULL,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", createTableSQL) } From 2ce94500d0001fb4d96fefb1a8ba064efc2f4d8c Mon Sep 17 00:00:00 2001 From: meiji163 Date: Fri, 27 Jun 2025 11:24:43 -0700 Subject: [PATCH 8/8] drop old table in migrator test --- go/logic/migrator_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/go/logic/migrator_test.go b/go/logic/migrator_test.go index 88c7b4323..86c060acf 100644 --- a/go/logic/migrator_test.go +++ b/go/logic/migrator_test.go @@ -336,6 +336,7 @@ func (suite *MigratorTestSuite) TestMigrateEmpty() { migrationContext.ApplierConnectionConfig = connectionConfig migrationContext.InspectorConnectionConfig = connectionConfig migrationContext.SetConnectionConfig("innodb") + migrationContext.InitiallyDropOldTable = true migrationContext.AlterStatementOptions = "ADD COLUMN foobar varchar(255), ENGINE=InnoDB"