Skip to content

Migrate source consumers to SourceRuntime (GTID failover 2/3)#452

Open
driv3r wants to merge 1 commit into
gtid-stage6-master-failoverfrom
gtid-stage7-migrate-source-runtime
Open

Migrate source consumers to SourceRuntime (GTID failover 2/3)#452
driv3r wants to merge 1 commit into
gtid-stage6-master-failoverfrom
gtid-stage7-migrate-source-runtime

Conversation

@driv3r

@driv3r driv3r commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

What

Migrates Ghostferry's source-side consumers to resolve their connection from SourceRuntime (introduced in the parent PR) instead of holding a captured *sql.DB.

PR 2 of 3 of the GTID master failover rework:

  1. Introduce SourceRuntime (Introduce SourceRuntime to own the source connection (GTID failover 1/3) #451).
  2. This PR — migrate source consumers to the runtime (behavior-neutral).
  3. GTID master failover implemented as a source swap + Ruby integration test.

Stacked on #451. Review/merge that first; this PR's diff is against it.

Why

With consumers holding their own *sql.DB, a source master failover would have to hand-edit a field on each one — brittle and easy to miss. Having each consumer ask the runtime for the current source inverts that: a failover becomes a single SourceRuntime.Replace, and every consumer that starts new work afterwards picks up the promoted writer automatically.

How

Each consumer keeps its static DB as a fallback when no runtime is configured, so today (no swap ever happens) behavior is unchanged.

  • CursorConfigNewCursor binds a cursor to the runtime's current handle at creation. A cursor keeps that handle for its whole scan (a scan must be consistent against one server); only cursors created after a swap observe the new source.
  • DataIterator — resolves the source once at the start of Run.
  • InlineVerifiercurrentSourceDB resolves the runtime handle and, on a detected swap, resets the source statement cache (cached prepared statements are bound to the previous DB). The DB and its matching cache are returned together under a lock so concurrent verification workers stay consistent.
  • IterativeVerifiercurrentSourceDB resolves the runtime handle for fingerprint / compressed-hash reads; its CursorConfig also carries the runtime for pagination scans.
  • ChecksumTableVerifiercurrentSourceDB resolves the runtime handle for CHECKSUM TABLE reads.

Ferry wires sourceRuntime into each constructor.

Testing

  • Unit tests for every resolver: static fallback, runtime resolution, cursor bind-at-creation + scan-consistency across a swap, inline verifier stmt-cache reset on swap; -race clean.
  • Full test/go integration suite passes in both file_position and gtid modes (confirming behavior neutrality — the runtime resolves to the same initial handle when no failover occurs).
  • go build + gofmt clean.

Have the source-side components resolve the current source connection
from the Ferry's SourceRuntime instead of holding a captured *sql.DB, so
a future source master failover can repoint them all by swapping the
runtime rather than editing each consumer.

Consumers migrated (each keeps its static DB as a fallback when no runtime
is configured, so behavior is unchanged today):
- CursorConfig: NewCursor binds a cursor to the runtime's current handle
  at creation time. A cursor keeps that handle for its whole scan (a scan
  must be consistent against one server); only cursors created after a
  swap observe the new source.
- DataIterator: resolves the source once at the start of Run.
- InlineVerifier: currentSourceDB resolves the runtime handle and, on a
  detected swap, resets the source statement cache (cached statements are
  bound to the previous DB); the DB and its matching cache are returned
  together under a lock so concurrent workers stay consistent.
- IterativeVerifier: currentSourceDB resolves the runtime handle for
  fingerprint/compressed-hash reads; its CursorConfig also carries the
  runtime for pagination scans.
- ChecksumTableVerifier: currentSourceDB resolves the runtime handle for
  CHECKSUM TABLE reads.

Ferry wires f.sourceRuntime into each constructor. No swap happens yet
(that arrives with failover), so the runtime always resolves to the same
initial handle and behavior is unchanged.
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