Repository navigation
Conversation
Add move-table command parsing and validate the table list input. Initialize migration context from the CLI configuration. Refs: #1702
Build range-bounded copy queries and prepare target-table DML queries. Apply copy batches through the applier and invoke them from the migrator. Refs: #1703
Enable end-to-end target-table copying, route DML events to the target, and harden move-table initialization and table selection. Refs: #1705 Co-authored-by: Chris Kirkland <chriskirkland@github.com> Co-authored-by: Daniel Joos <danieljoos@github.com> Co-authored-by: womoruyi <womoruyi@github.com>
Implement the T0-T6 cooperative cutover workflow and cover atomic target-table cutover behavior. Refs: #1704 Co-authored-by: Zach Sierakowski <zacharysierakowski@github.com>
Create target tables in the destination database and abort safely when target tables already exist. Refs: #1710
Use target-cluster credentials and replica status for control-replica throttling during move-table migrations. Refs: #1709
Expose move-table source, target, and migration details to lifecycle hooks. Refs: #1711
Initialize move-table configuration consistently before migration setup. Refs: #1712
Enable the throttler in move-table mode with the move-table-specific configuration. Refs: #1713
Persist per-table checkpoints and drain GTIDs so copying and cutover can resume safely after interruption. Refs: #1708
Remove target artifacts and migration state after successful or failed move-table operations. Refs: #1717
Add local integration scenarios for single-table copying and concurrent writes. Refs: #1714
Restore the move-table drain GTID when resuming an interrupted migration. Refs: #1720
Introduce source-primary connection configuration and guard primary-required move-table operations. Refs: #1718
Update status reporting and lag measurement for move-table migrations. Refs: #1721
Add failpoint-driven integration coverage for copy, drain, hook, and cutover recovery. Refs: #1723
Extend migration state, copying, checkpointing, draining, and atomic rename to ordered sets of tables. Refs: #1726
Exclude generated columns from move-tables writable column lists during initial setup and resume. Use the filtered columns for row-copy queries while preserving the complete column layout for binlog event ordinals.
Populate writable column metadata for fresh and resumed move-table migrations so JSON, unsigned, and binary values are encoded correctly.
Exercise row copy and concurrent DML with signed, unsigned, and maximum BIGINT UNSIGNED values.
5acafd2 to
f830fe0
Compare
There was a problem hiding this comment.
Pull request overview
Adds multi-table, GTID-based migration between MySQL clusters, including target-side copying, cooperative cutover, recovery, throttling, hooks, and integration coverage.
Changes:
- Adds move-table configuration, copy/application flow, and atomic cutover.
- Adds checkpoint-based recovery and multi-table runtime state.
- Adds dual-cluster integration tooling and failpoint tests.
Show a summary per file
Review details
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Files reviewed: 78/93 changed files
- Comments generated: 4
- Review effort level: Balanced
| if apl.migrationContext.IsMoveTablesMode() { | ||
| lockTable = "movetables." + apl.migrationContext.MoveTablesRunToken() | ||
| lockSubject = fmt.Sprintf("tables %v", apl.migrationContext.MoveTables.TableNames) | ||
| } else { | ||
| lockTable = apl.originalTableName() | ||
| lockSubject = fmt.Sprintf("`%s`.`%s`", apl.migrationContext.DatabaseName, apl.originalTableName()) | ||
| } | ||
| lockName := buildMigrationLockName(apl.migrationContext.GetTargetDatabaseName(), lockTable) |
| applied := coords.SmallerThanOrEquals(mgtr.applier.CurrentCoordinates) | ||
| idle := len(mgtr.applyEventsQueue) == 0 && (mgtr.eventsStreamer == nil || len(mgtr.eventsStreamer.eventsChannel) == 0) | ||
| if applied || idle { | ||
| if !applied { | ||
| mgtr.applier.CurrentCoordinates = coords.Clone() | ||
| } |
| if mgtr.migrationContext.Checkpoint { | ||
| if err := mgtr.persistMoveTablesCutOverCheckpoint(drainGTID, false); err != nil { | ||
| return fmt.Errorf("failed to persist move-tables cutover checkpoint: %w", err) | ||
| } |
| if err := mgtr.applier.CreateCheckpointTable(); err != nil { | ||
| mgtr.migrationContext.Log.Errorf("unable to create checkpoint table, see further error details") | ||
| } |
|
|
||
| // moveTablesCutOver orchestrates the cooperative cutover protocol for move-tables | ||
| // mode. It implements the T0-T6 transitions described in | ||
| // docs/learning/design-refs/coop_cutover.md §1.3. |
There was a problem hiding this comment.
there are still some references to our internal poc docs in here. e.g. all the § refs.
Maybe it would also make sense to add a condensed version of the design document to the docs folder to describe what the move-tables mode actually does and how the cutover process is supposed to work.
There was a problem hiding this comment.
Agreed. I’ll address the correctness findings directly in this PR and add the consolidated move-tables design documentation in a follow-up stacked PR based on this branch. I’ll link that PR here when it is opened.
|
This is intriguing; can you perhaps elaborate on how this compares to ghostferry and the rationale for building this into gh-ost rather than as a standalone tool? |
|
Hi @ggilder, I proposed extending gh-ost to support moving tables between clusters so we can build on tooling and operational practices that are already proven at GitHub. Before starting this work, I evaluated Ghostferry, but found that adopting it would still require substantial integration effort. Ghostferry is designed primarily as a library, with gh-ost has powered GitHub’s schema changes for a decade and already provides most of the primitives needed for this workflow. It is trusted by application developers, familiar to (and maintained by) the database team, and backed by build and test infrastructure in which we have invested heavily. Its primary limitation was the assumption that source and target tables reside in the same cluster, which is easy to resolve. Making move tables a first-class gh-ost feature lets us reuse those primitives and its existing hook system, rather than creating and operating a separate migration binary. It also gives us a natural path to integrate table moves with our existing schema-change automation, allowing online schema changes and cross-cluster table moves to be offered through the same platform workflow. |

Description
This PR adds support for a new move table mode, in which one or more tables are copied from a source MySQL cluster to a target MySQL cluster.
Related issue: #1681
script/cibuildreturns with no formatting errors, build errors or unit test errors.Component PRs
Most of the PRs for this feature were created before Stacked PR were available. In addition to the PR stack, this feature branch also consolidates the following move-tables PRs:
Core migration flow
Cutover and recovery
Throttling, hooks, and cleanup
Integration coverage and follow-up