diff --git a/go/base/context.go b/go/base/context.go index ea5ab6805..dfc69bbdd 100644 --- a/go/base/context.go +++ b/go/base/context.go @@ -83,13 +83,14 @@ type MigrationContext struct { ServeSocketFile string ServeTCPPort int64 - Noop bool - TestOnReplica bool - MigrateOnReplica bool - OkToDropTable bool - InitiallyDropOldTable bool - InitiallyDropGhostTable bool - CutOverType CutOver + Noop bool + TestOnReplica bool + MigrateOnReplica bool + TestOnReplicaSkipReplicaStop bool + OkToDropTable bool + InitiallyDropOldTable bool + InitiallyDropGhostTable bool + CutOverType CutOver TableEngine string RowsEstimate int64 diff --git a/go/cmd/gh-ost/main.go b/go/cmd/gh-ost/main.go index b61483134..512c537b9 100644 --- a/go/cmd/gh-ost/main.go +++ b/go/cmd/gh-ost/main.go @@ -61,6 +61,7 @@ func main() { executeFlag := flag.Bool("execute", false, "actually execute the alter & migrate the table. Default is noop: do some tests and exit") flag.BoolVar(&migrationContext.TestOnReplica, "test-on-replica", false, "Have the migration run on a replica, not on the master. At the end of migration replication is stopped, and tables are swapped and immediately swap-revert. Replication remains stopped and you can compare the two tables for building trust") + flag.BoolVar(&migrationContext.TestOnReplicaSkipReplicaStop, "test-on-replica-skip-replica-stop", false, "When --test-on-replica is enabled, do not issue commands stop replication (requires --test-on-replica)") flag.BoolVar(&migrationContext.MigrateOnReplica, "migrate-on-replica", false, "Have the migration run on a replica, not on the master. This will do the full migration on the replica including cut-over (as opposed to --test-on-replica)") flag.BoolVar(&migrationContext.OkToDropTable, "ok-to-drop-table", false, "Shall the tool drop the old table at end of operation. DROPping tables can be a long locking operation, which is why I'm not doing it by default. I'm an online tool, yes?") @@ -149,6 +150,13 @@ func main() { if migrationContext.SwitchToRowBinlogFormat && migrationContext.AssumeRBR { log.Fatalf("--switch-to-rbr and --assume-rbr are mutually exclusive") } + if migrationContext.TestOnReplicaSkipReplicaStop { + if !migrationContext.TestOnReplica { + log.Fatalf("--test-on-replica-skip-replica-stop requires --test-on-replica to be enabled") + } + log.Warning("--test-on-replica-skip-replica-stop enabled. We will not stop replication before cut-over. Ensure you have a plugin that does this.") + } + switch *cutOver { case "atomic", "default", "": migrationContext.CutOverType = base.CutOverAtomic diff --git a/go/logic/applier.go b/go/logic/applier.go index a122b7173..1d66db027 100644 --- a/go/logic/applier.go +++ b/go/logic/applier.go @@ -574,6 +574,7 @@ func (this *Applier) StopReplication() error { if err := this.StopSlaveSQLThread(); err != nil { return err } + readBinlogCoordinates, executeBinlogCoordinates, err := mysql.GetReplicationBinlogCoordinates(this.db) if err != nil { return err diff --git a/go/logic/migrator.go b/go/logic/migrator.go index 276cc37a6..65d869ac6 100644 --- a/go/logic/migrator.go +++ b/go/logic/migrator.go @@ -478,9 +478,14 @@ func (this *Migrator) cutOver() (err error) { // the same cut-over phase as the master would use. That means we take locks // and swap the tables. // The difference is that we will later swap the tables back. - log.Debugf("testing on replica. Stopping replication IO thread") - if err := this.retryOperation(this.applier.StopReplication); err != nil { - return err + + if this.migrationContext.TestOnReplicaSkipReplicaStop { + log.Warningf("--test-on-replica-skip-replica-stop enabled, we are not stopping replication.") + } else { + log.Debugf("testing on replica. Stopping replication IO thread") + if err := this.retryOperation(this.applier.StopReplication); err != nil { + return err + } } // We're merly testing, we don't want to keep this state. Rollback the renames as possible defer this.applier.RenameTablesRollback()