Skip to content

Commit 1be6a4c

Browse files
committed
Attempt instant DDL if supported
1 parent 9b3fa79 commit 1be6a4c

File tree

4 files changed

+76
-0
lines changed

4 files changed

+76
-0
lines changed

go/base/context.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ type MigrationContext struct {
101101
AliyunRDS bool
102102
GoogleCloudPlatform bool
103103
AzureMySQL bool
104+
AttemptInstantDDL bool
104105

105106
config ContextConfig
106107
configMutex *sync.Mutex

go/cmd/gh-ost/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ func main() {
6767
flag.StringVar(&migrationContext.DatabaseName, "database", "", "database name (mandatory)")
6868
flag.StringVar(&migrationContext.OriginalTableName, "table", "", "table name (mandatory)")
6969
flag.StringVar(&migrationContext.AlterStatement, "alter", "", "alter statement (mandatory)")
70+
flag.BoolVar(&migrationContext.AttemptInstantDDL, "attempt-instant-ddl", true, "Attempt to use instant DDL for this migration first.")
71+
7072
flag.BoolVar(&migrationContext.CountTableRows, "exact-rowcount", false, "actually count table rows as opposed to estimate them (results in more accurate progress estimation)")
7173
flag.BoolVar(&migrationContext.ConcurrentCountTableRows, "concurrent-rowcount", true, "(with --exact-rowcount), when true (default): count rows after row-copy begins, concurrently, and adjust row estimate later on; when false: first count rows, then start row copy")
7274
flag.BoolVar(&migrationContext.AllowedRunningOnMaster, "allow-on-master", false, "allow this migration to run directly on master. Preferably it would run on a replica")

go/logic/applier.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,54 @@ func (this *Applier) ValidateOrDropExistingTables() error {
188188
return nil
189189
}
190190

191+
// AttemptInstantDDL attempts to use instant DDL (from MySQL 8.0, and earlier in Aurora and some others.)
192+
// to apply the ALTER statement immediately. If it errors, the original
193+
// gh-ost algorithm can be used. However, if it's successful -- a lot
194+
// of time can potentially be saved. Instant operations include:
195+
// - Adding a column
196+
// - Dropping a column
197+
// - Dropping an index
198+
// - Extending a varchar column
199+
// It is safer to attempt the change than try and parse the DDL, since
200+
// there might be specifics about the table which make it not possible to apply instantly.
201+
func (this *Applier) AttemptInstantDDL() error {
202+
203+
query := fmt.Sprintf(`ALTER /* gh-ost */ TABLE %s.%s %s, ALGORITHM=INSTANT`,
204+
sql.EscapeName(this.migrationContext.DatabaseName),
205+
sql.EscapeName(this.migrationContext.OriginalTableName),
206+
this.migrationContext.AlterStatementOptions,
207+
)
208+
this.migrationContext.Log.Infof("INSTANT DDL Query is: %s", query)
209+
210+
err := func() error {
211+
tx, err := this.db.Begin()
212+
if err != nil {
213+
return err
214+
}
215+
defer tx.Rollback()
216+
217+
sessionQuery := fmt.Sprintf(`SET SESSION time_zone = '%s'`, this.migrationContext.ApplierTimeZone)
218+
sessionQuery = fmt.Sprintf("%s, %s", sessionQuery, this.generateSqlModeQuery())
219+
220+
if _, err := tx.Exec(sessionQuery); err != nil {
221+
return err
222+
}
223+
if _, err := tx.Exec(query); err != nil {
224+
this.migrationContext.Log.Infof("INSTANT DDL failed: %s", err)
225+
return err
226+
}
227+
if err := tx.Commit(); err != nil {
228+
// Neither SET SESSION nor ALTER are really transactional, so strictly speaking
229+
// there's no need to commit; but let's do this the legit way anyway.
230+
return err
231+
}
232+
return nil
233+
}()
234+
235+
return err
236+
237+
}
238+
191239
// CreateGhostTable creates the ghost table on the applier host
192240
func (this *Applier) CreateGhostTable() error {
193241
query := fmt.Sprintf(`create /* gh-ost */ table %s.%s like %s.%s`,

go/logic/migrator.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,12 +351,26 @@ func (this *Migrator) Migrate() (err error) {
351351
if err := this.initiateInspector(); err != nil {
352352
return err
353353
}
354+
// In MySQL 8.0 (and possibly earlier) some DDL statements can be applied instantly.
355+
// As just a metadata change. We can't detect this unless we attempt the statement
356+
// (i.e. there is no explain for DDL).
357+
if this.migrationContext.AttemptInstantDDL {
358+
this.migrationContext.Log.Infof("Attempting to execute ALTER TABLE as INSTANT DDL")
359+
if err := this.attemptInstantDDL(); err == nil {
360+
this.migrationContext.Log.Infof("Success! Table %s.%s migrated instantly", sql.EscapeName(this.migrationContext.DatabaseName), sql.EscapeName(this.migrationContext.OriginalTableName))
361+
return nil
362+
} else {
363+
this.migrationContext.Log.Infof("INSTANT DDL failed, will proceed with original algorithm: %s", err)
364+
}
365+
}
366+
354367
if err := this.initiateStreaming(); err != nil {
355368
return err
356369
}
357370
if err := this.initiateApplier(); err != nil {
358371
return err
359372
}
373+
360374
if err := this.createFlagFiles(); err != nil {
361375
return err
362376
}
@@ -734,6 +748,17 @@ func (this *Migrator) initiateServer() (err error) {
734748
return nil
735749
}
736750

751+
// attemptInstantDDL tries to apply the DDL statement directly to the table
752+
// using a ALGORITHM=INSTANT assertion. If this fails, it will return an error,
753+
// in which case the original algorithm should be used.
754+
func (this *Migrator) attemptInstantDDL() (err error) {
755+
this.applier = NewApplier(this.migrationContext)
756+
if err := this.applier.InitDBConnections(); err != nil {
757+
return err
758+
}
759+
return this.applier.AttemptInstantDDL()
760+
}
761+
737762
// initiateInspector connects, validates and inspects the "inspector" server.
738763
// The "inspector" server is typically a replica; it is where we issue some
739764
// queries such as:

0 commit comments

Comments
 (0)