Skip to content

Commit cc2dd7f

Browse files
authored
Add flag --skip-metadata-lock-check (#1616)
Since #1536, performance_schema.metadata_locks is required to check the rename session holds the metadata lock on the migrated table during cut-over. On some setups such as Aurora RDS performance_schema is not enabled by default and it may be infeasible to enable. This PR adds --skip-metadata-lock-check flag to skip the check in this case.
1 parent 982eefb commit cc2dd7f

File tree

6 files changed

+33
-4
lines changed

6 files changed

+33
-4
lines changed

doc/command-line-flags.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ By default, `gh-ost` would like you to connect to a replica, from where it figur
2424

2525
If, for some reason, you do not wish `gh-ost` to connect to a replica, you may connect it directly to the master and approve this via `--allow-on-master`.
2626

27+
### allow-setup-metadata-lock-instruments
28+
29+
`--allow-setup-metadata-lock-instruments` allows gh-ost to enable the [`metadata_locks`](https://dev.mysql.com/doc/refman/8.0/en/performance-schema-metadata-locks-table.html) table in `performance_schema`, if it is not already enabled. This is used for a safety check before cut-over.
30+
See also: [`skip-metadata-lock-check`](#skip-metadata-lock-check)
31+
2732
### approve-renamed-columns
2833

2934
When your migration issues a column rename (`change column old_name new_name ...`) `gh-ost` analyzes the statement to try and associate the old column name with new column name. Otherwise, the new structure may also look like some column was dropped and another was added.
@@ -247,6 +252,13 @@ Defaults to an auto-determined and advertised upon startup file. Defines Unix so
247252

248253
By default `gh-ost` verifies no foreign keys exist on the migrated table. On servers with large number of tables this check can take a long time. If you're absolutely certain no foreign keys exist (table does not reference other table nor is referenced by other tables) and wish to save the check time, provide with `--skip-foreign-key-checks`.
249254

255+
### skip-metadata-lock-check
256+
257+
By default `gh-ost` performs a check before the cut-over to ensure the rename session holds the exclusive metadata lock on the table. In case `performance_schema.metadata_locks` cannot be enabled on your setup, this check can be skipped with `--skip-metadata-lock-check`.
258+
:warning: Disabling this check involves the small chance of data loss in case a session accesses the ghost table during cut-over. See https://github.com/github/gh-ost/pull/1536 for details.
259+
260+
See also: [`allow-setup-metadata-lock-instruments`](#allow-setup-metadata-lock-instruments)
261+
250262
### skip-strict-mode
251263

252264
By default `gh-ost` enforces STRICT_ALL_TABLES sql_mode as a safety measure. In some cases this changes the behaviour of other modes (namely ERROR_FOR_DIVISION_BY_ZERO, NO_ZERO_DATE, and NO_ZERO_IN_DATE) which may lead to errors during migration. Use `--skip-strict-mode` to explicitly tell `gh-ost` not to enforce this. **Danger** This may have some unexpected disastrous side effects.

go/base/context.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ type MigrationContext struct {
256256

257257
BinlogSyncerMaxReconnectAttempts int
258258
AllowSetupMetadataLockInstruments bool
259+
SkipMetadataLockCheck bool
259260
IsOpenMetadataLockInstruments bool
260261

261262
Log Logger

go/cmd/gh-ost/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ func main() {
138138
flag.Int64Var(&migrationContext.HooksStatusIntervalSec, "hooks-status-interval", 60, "how many seconds to wait between calling onStatus hook")
139139

140140
flag.UintVar(&migrationContext.ReplicaServerId, "replica-server-id", 99999, "server id used by gh-ost process. Default: 99999")
141-
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")
141+
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")
142+
flag.BoolVar(&migrationContext.SkipMetadataLockCheck, "skip-metadata-lock-check", false, "Skip metadata lock check at cut-over time. The checks require performance_schema.metadata_lock to be enabled")
142143
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")
143144

144145
flag.BoolVar(&migrationContext.IncludeTriggers, "include-triggers", false, "When true, the triggers (if exist) will be created on the new table")

go/logic/applier.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,10 +483,16 @@ func (this *Applier) dropTable(tableName string) error {
483483
return nil
484484
}
485485

486+
// StateMetadataLockInstrument checks if metadata_locks is enabled in performance_schema.
487+
// If not it attempts to enable metadata_locks if this is allowed.
486488
func (this *Applier) StateMetadataLockInstrument() error {
487489
query := `select /*+ MAX_EXECUTION_TIME(300) */ ENABLED, TIMED from performance_schema.setup_instruments WHERE NAME = 'wait/lock/metadata/sql/mdl'`
488490
var enabled, timed string
489491
if err := this.db.QueryRow(query).Scan(&enabled, &timed); err != nil {
492+
if errors.Is(err, gosql.ErrNoRows) {
493+
// performance_schema may be disabled.
494+
return nil
495+
}
490496
return this.migrationContext.Log.Errorf("query performance_schema.setup_instruments with name wait/lock/metadata/sql/mdl error: %s", err)
491497
}
492498
if strings.EqualFold(enabled, "YES") && strings.EqualFold(timed, "YES") {
@@ -1344,7 +1350,7 @@ func (this *Applier) AtomicCutOverMagicLock(sessionIdChan chan int64, tableLocke
13441350

13451351
this.migrationContext.Log.Infof("Session renameLockSessionId is %+v", *renameLockSessionId)
13461352
// Checking the lock is held by rename session
1347-
if *renameLockSessionId > 0 && this.migrationContext.IsOpenMetadataLockInstruments {
1353+
if *renameLockSessionId > 0 && this.migrationContext.IsOpenMetadataLockInstruments && !this.migrationContext.SkipMetadataLockCheck {
13481354
sleepDuration := time.Duration(10*this.migrationContext.CutOverLockTimeoutSeconds) * time.Millisecond
13491355
for i := 1; i <= 100; i++ {
13501356
err := this.ExpectMetadataLock(*renameLockSessionId)

go/logic/migrator.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1364,10 +1364,18 @@ func (this *Migrator) initiateApplier() error {
13641364
}
13651365
this.applier.WriteChangelogState(string(GhostTableMigrated))
13661366
}
1367+
1368+
// ensure performance_schema.metadata_locks is available.
13671369
if err := this.applier.StateMetadataLockInstrument(); err != nil {
1368-
this.migrationContext.Log.Errorf("Unable to enable metadata lock instrument, see further error details. Bailing out")
1369-
return err
1370+
this.migrationContext.Log.Warning("Unable to enable metadata lock instrument, see further error details.")
13701371
}
1372+
if !this.migrationContext.IsOpenMetadataLockInstruments {
1373+
if !this.migrationContext.SkipMetadataLockCheck {
1374+
return this.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.")
1375+
}
1376+
this.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.")
1377+
}
1378+
13711379
go this.applier.InitiateHeartbeat()
13721380
return nil
13731381
}

localtests/test.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ test_single() {
287287
--alter='engine=${storage_engine}' \
288288
--exact-rowcount \
289289
--assume-rbr \
290+
--skip-metadata-lock-check \
290291
--initially-drop-old-table \
291292
--initially-drop-ghost-table \
292293
--throttle-query='select timestampdiff(second, min(last_update), now()) < 5 from _${table_name}_ghc' \

0 commit comments

Comments
 (0)