Skip to content

GTID 5/5: GTID-aware replica catchup#449

Open
driv3r wants to merge 4 commits into
gtid-stage4-state-resumefrom
gtid-stage5-replica-progress
Open

GTID 5/5: GTID-aware replica catchup#449
driv3r wants to merge 4 commits into
gtid-stage4-state-resumefrom
gtid-stage5-replica-progress

Conversation

@driv3r

@driv3r driv3r commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Stacked PR 5 of 5 — GTID support

Base: #448. Final PR in the stack.

What this PR does

Makes WaitUntilReplicaIsCaughtUpToMaster coordinate-mode aware so running Ghostferry from a replica works in GTID mode, using executed-set containment instead of file/position ordering.

  • Adds ReplicatedMasterCoordinateFetcher + ReplicatedMasterGTIDViaCustomQuery, and IsCaughtUpToCoordinate (GTID containment / file-pos compare). The legacy file/position fetcher and IsCaughtUp API are preserved via an adapter.
  • Ferry passes BinlogCoordinateMode to the wait object; the init probe uses a mode-appropriate coordinate.
  • copydb and sharding gain ReplicatedMasterGTIDQuery and select the GTID fetcher in GTID mode.
  • Tests: GTID replica-catchup suite; replication-config and master-change tests made mode-aware.

Verified locally on MySQL 8.0: full Go and Ruby suites pass in both file_position and gtid modes.


Whole-stack validation summary

  • test/go: pass in both modes; copydb/test + sharding/test pass.
  • Ruby suite: file_position 73 tests/650 assertions, gtid 73 tests/651 assertions — 0 failures.

@driv3r
driv3r force-pushed the gtid-stage4-state-resume branch from 51a31ee to 5e4845c Compare July 22, 2026 00:17
@driv3r
driv3r force-pushed the gtid-stage5-replica-progress branch from 1fe1cb0 to 183b91b Compare July 22, 2026 00:17
@driv3r
driv3r force-pushed the gtid-stage4-state-resume branch from 5e4845c to aa1df3b Compare July 22, 2026 13:50
@driv3r
driv3r force-pushed the gtid-stage5-replica-progress branch from 183b91b to c4cbc01 Compare July 22, 2026 13:50
@driv3r
driv3r requested review from a team, milanatshopify and pawandubey July 22, 2026 16:07
driv3r added 4 commits July 22, 2026 20:50
Make WaitUntilReplicaIsCaughtUpToMaster coordinate-mode aware so running
from a replica works in GTID mode, using executed-set containment
instead of file/position ordering.

- Add ReplicatedMasterCoordinateFetcher + ReplicatedMasterGTIDViaCustomQuery,
  and IsCaughtUpToCoordinate (GTID containment / file-pos compare). The
  legacy file/position fetcher and IsCaughtUp API are preserved via an
  adapter.
- Ferry passes BinlogCoordinateMode to the wait object; the init probe
  uses a mode-appropriate coordinate.
- copydb and sharding gain ReplicatedMasterGTIDQuery and select the
  GTID fetcher in GTID mode.
- Tests: GTID replica-catchup suite; replication-config and
  master-change tests made mode-aware.

Verified locally on MySQL 8.0: full Go and Ruby suites pass in both
file_position and gtid modes.
IsCaughtUpToCoordinate no longer branches on coordinate type; it asks
current.HasReached(targetMaster), delegating file/position-vs-GTID
mechanics to BinlogCoordinate.
- copydb and sharding now reject an empty ReplicatedMasterGTIDQuery (and
  copydb an empty ReplicatedMasterPositionQuery) when running from a
  replica, failing at init instead of late at the first probe.
- When in GTID mode, validate @@GLOBAL.gtid_mode=ON on the source
  replication master, since its executed GTID set is the cutover target.
The config comment predated GTID streaming/state/resume/replica wiring
and claimed streaming still used file/position. Describe the actual
shipped behavior, the gtid_mode requirements, and that legacy
file/position progress fields are not populated in GTID mode (use the
coordinate fields instead).
@driv3r
driv3r force-pushed the gtid-stage4-state-resume branch from aa1df3b to 53678c2 Compare July 22, 2026 19:00
@driv3r
driv3r force-pushed the gtid-stage5-replica-progress branch from c4cbc01 to a00df91 Compare July 22, 2026 19:00
@driv3r

driv3r commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

Review follow-ups addressed (stack-wide)

Aggregated the three reviews and pushed fixes to the relevant stage branches (rebased through the stack). Summary:

#447 (gtid-stage3-streamer)

  • Empty-set stop hang (High): shouldContinueStreaming no longer treats an empty stop coordinate as "unset". FlushAndStop always records the target before stopRequested, so an empty executed set on a fresh source is a valid, already-reached target instead of an infinite wait.
  • Non-XID GTID advancement (High): DDL/admin QueryEvents (which commit without an XIDEvent) now advance the streamed GTID set; BEGIN/COMMIT/ROLLBACK are skipped. Prevents cutover hangs when the trailing statement is DDL.
  • gtid_mode validation (Medium): CheckServerGTIDModeEnabled is now wired into Ferry.Initialize for source and (when verifying) target.

#446 (gtid-stage2-coordinate-mode)

  • Reparse on hot path (Medium): added NewGTIDCoordinateFromSet, which caches the parsed mysql.GTIDSet; HasReached reuses it, so the per-event stop check no longer serializes+reparses. Cache is unexported / not serialized.

#448 (gtid-stage4-state-resume)

  • Resume mode mismatch (High): Ferry.Start rejects resuming when the state's coordinate mode differs from the configured mode.
  • Empty-set target-verifier resume (High): presence is checked via HasTargetVerifierBinlogCoordinate() (pointer/field), not IsZero(), so an empty GTID coordinate isn't mistaken for absent.
  • Fail-closed intersection (Medium): intersectGTIDSets/MinSourceBinlogCoordinate return errors on parse/set-math failure instead of silently falling back to one side; Ferry.Start propagates.
  • Interface breakage (Low/Med): SetCoordinates removed from the exported DMLEvent interface; stamping uses an internal coordinateStamper assertion.
  • CI (Medium): go-test now runs gtid on MySQL 8.0/8.4 (file_position on all).

#449 (gtid-stage5-replica-progress)

  • Empty ReplicatedMasterGTIDQuery (and copydb's position query) rejected at init.
  • Master @@GLOBAL.gtid_mode=ON validated in GTID mode.
  • Docs: BinlogCoordinateMode comment updated for shipped behavior; documents that legacy file/position progress fields are not populated in GTID mode (use coordinate fields).

Added tests: empty stop target, QueryEvent advancement, transaction-control detection, NewGTIDCoordinateFromSet clone/cache + JSON exclusion, multi-UUID intersection, fail-closed parse error, target-verifier presence with empty set.

Validation (MySQL 8.0.32): full Go test/go + copydb + sharding pass in both modes; full Ruby suite passes in both modes (file_position 73/650, gtid 73/651). Build + gofmt clean.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant