From 7d0ec9c9dcc8bf0c7af53de7d8e50809df016568 Mon Sep 17 00:00:00 2001 From: Shlomi Noach Date: Wed, 15 Jun 2016 12:18:59 +0200 Subject: [PATCH] added --migrate-on-replica flag; runs complete migration on replica --- go/base/context.go | 1 + go/cmd/gh-ost/main.go | 11 ++++++++++- go/logic/migrator.go | 8 ++++---- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/go/base/context.go b/go/base/context.go index 402bde6..3132220 100644 --- a/go/base/context.go +++ b/go/base/context.go @@ -74,6 +74,7 @@ type MigrationContext struct { Noop bool TestOnReplica bool + MigrateOnReplica bool OkToDropTable bool InitiallyDropOldTable bool InitiallyDropGhostTable bool diff --git a/go/cmd/gh-ost/main.go b/go/cmd/gh-ost/main.go index dca4814..38c4b4a 100644 --- a/go/cmd/gh-ost/main.go +++ b/go/cmd/gh-ost/main.go @@ -57,7 +57,9 @@ func main() { flag.BoolVar(&migrationContext.NullableUniqueKeyAllowed, "allow-nullable-unique-key", false, "allow gh-ost to migrate based on a unique key with nullable columns. As long as no NULL values exist, this should be OK. If NULL values exist in chosen key, data may be corrupted. Use at your own risk!") 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 tables are not swapped; gh-ost issues `STOP SLAVE` and you can compare the two tables for building trust") + 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.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?") flag.BoolVar(&migrationContext.InitiallyDropOldTable, "initially-drop-old-table", false, "Drop a possibly existing OLD table (remains from a previous run?) before beginning operation. Default is to panic and abort if such table exists") flag.BoolVar(&migrationContext.InitiallyDropGhostTable, "initially-drop-ghost-table", false, "Drop a possibly existing Ghost table (remains from a previous run?) before beginning operation. Default is to panic and abort if such table exists") @@ -127,6 +129,13 @@ func main() { if migrationContext.AllowedRunningOnMaster && migrationContext.TestOnReplica { log.Fatalf("--allow-on-master and --test-on-replica are mutually exclusive") } + if migrationContext.AllowedRunningOnMaster && migrationContext.MigrateOnReplica { + log.Fatalf("--allow-on-master and --migrate-on-replica are mutually exclusive") + } + if migrationContext.MigrateOnReplica && migrationContext.TestOnReplica { + log.Fatalf("--migrate-on-replica and --test-on-replica are mutually exclusive") + } + switch *cutOver { case "safe", "default", "": migrationContext.CutOverType = base.CutOverSafe diff --git a/go/logic/migrator.go b/go/logic/migrator.go index 7cd0e3d..fda5a59 100644 --- a/go/logic/migrator.go +++ b/go/logic/migrator.go @@ -118,7 +118,7 @@ func (this *Migrator) shouldThrottle() (result bool, reason string) { if time.Duration(lag) > time.Duration(this.migrationContext.MaxLagMillisecondsThrottleThreshold)*time.Millisecond { return true, fmt.Sprintf("lag=%fs", time.Duration(lag).Seconds()) } - if this.migrationContext.TestOnReplica && (atomic.LoadInt64(&this.allEventsUpToLockProcessedInjectedFlag) == 0) { + if (this.migrationContext.TestOnReplica || this.migrationContext.MigrateOnReplica) && (atomic.LoadInt64(&this.allEventsUpToLockProcessedInjectedFlag) == 0) { replicationLag, err := mysql.GetMaxReplicationLag(this.migrationContext.InspectorConnectionConfig, this.migrationContext.ThrottleControlReplicaKeys, this.migrationContext.ReplictionLagQuery) if err != nil { return true, err.Error() @@ -666,11 +666,11 @@ func (this *Migrator) initiateInspector() (err error) { if this.migrationContext.ApplierConnectionConfig, err = this.inspector.getMasterConnectionConfig(); err != nil { return err } - if this.migrationContext.TestOnReplica { + if this.migrationContext.TestOnReplica || this.migrationContext.MigrateOnReplica { if this.migrationContext.InspectorIsAlsoApplier() { - return fmt.Errorf("Instructed to --test-on-replica, but the server we connect to doesn't seem to be a replica") + return fmt.Errorf("Instructed to --test-on-replica or --migrate-on-replica, but the server we connect to doesn't seem to be a replica") } - log.Infof("--test-on-replica given. Will not execute on master %+v but rather on replica %+v itself", + log.Infof("--test-on-replica or --migrate-on-replica given. Will not execute on master %+v but rather on replica %+v itself", this.migrationContext.ApplierConnectionConfig.Key, this.migrationContext.InspectorConnectionConfig.Key, ) this.migrationContext.ApplierConnectionConfig = this.migrationContext.InspectorConnectionConfig.Duplicate()