Skip to content

POC: copy cutover dependencies by logical identity - #462

Draft
rogeryen wants to merge 2 commits into
Shopify:mainfrom
rogeryen:rogeryen/poc-cutover-dependencies
Draft

rogeryen wants to merge 2 commits into
Shopify:mainfrom
rogeryen:rogeryen/poc-cutover-dependencies

Conversation

@rogeryen

@rogeryen rogeryen commented Oct 9, 2026 •

Copy link
Copy Markdown

Summary

Draft proof of concept for copying small, referenced dependency sets during sharding cutover without adding a sharding-key column to those tables.

Separate reference selection, source pagination, and destination logical identity. A dependency can retain a destination-local surrogate ID even when the equivalent source row has a different ID.

Behavior

  • Add optional CutoverDependencies configuration. No behavior changes without it.
  • Exclude explicit dependency tables from normal row copying, binlog streaming, and pagination-based verification.
  • Run after the cutover lock, source replica catch-up/binlog flush, and existing joined-table delta copy.
  • Select rows with composite joins and parameterized equality predicates; paginate on the source integer primary key.
  • Use one source snapshot and one target transaction for all dependency tables.
  • Preserve existing target rows on duplicate-key inserts, then verify by a separate unique logical key. Compare every column except the surrogate pagination key, including nullness.
  • Fail closed on missing required dependencies, conflicting payloads, unmatched surrogate collisions, unsupported schemas, or row/byte/time budget violations.
  • Do not overwrite payloads, delete destination dependencies, regenerate IDs, or rewrite references.
  • Never invoke the success unlock callback after a dependency-copy failure.
  • Include the new sharding package tests in make test-go and the existing MySQL CI matrix.

See docs/cutover-dependencies.md for a fictional configuration example, the callback contract, and the remaining work.

Explicit POC limits

Not ready for production enablement.

The caller must stop relevant source and destination writers, including cleanup, until route switch. An HTTP lock callback's presence does not prove that application contract. This PR changes no caller configuration and enables no application tables.

The prototype requires matching InnoDB schemas, no target triggers/generated columns, a single integer pagination primary key, and a separate non-nullable full-column unique logical key. Payload comparison is intentionally strict, including timestamps. Query plans and write-stop latency still need evaluation with representative data. Source-replica and application-level concurrent-writer tests remain follow-ups. This is not a live dependency replication mechanism and does not apply to other migration backends.

Validation

Against local MySQL 8.0.32 source and target instances:

  • go test ./sharding -count=3 -timeout=90s — PASS.
  • go test ./sharding/test -count=1 -timeout=150s — PASS, existing sharding suite.
  • Focused dependency tests under -race, excluding the full streamer lifecycle — PASS.
  • go vet -composites=false ./sharding/... — PASS.
  • git diff --check — PASS.
  • go test -vet=off ./... -run '^$' — PASS, compile-only check.

Coverage includes different destination IDs, composite identity/joins, duplicate references, other tenants, released-row exclusion, multiple modules, idempotent retry, binary/null values, keyset batches, atomic rollback, missing dependencies, conflicts, schema restrictions, budgets, and a blocked-write deadline. Full lifecycle tests exercise a last in-flight write in the lock callback and prove that a conflict does not reach the success unlock callback.

Existing baseline issues, not changed here:

  • Full lifecycle -race reports unsynchronized binlog-streamer state. The existing TestSelectiveCopyDataWithoutAnyWritesToSource also reproduces the streamer stop races without this patch.
  • Default go vet reports an existing unkeyed MetricTag literal in sharding/filter.go.
  • go test ./... -run '^$' hits an existing vet error at testhelpers/integration_test_case.go:124 (Fatalf with a non-constant format string).

No full repository test-suite or production rollout claim.

This branch has not been deployed

No deployments
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