Skip to content

feat(database): add MySQL point-in-time recovery - #1144

Open
paulocsanz wants to merge 1 commit into
masterfrom
pcs/mysql-pitr-cli
Open

feat(database): add MySQL point-in-time recovery#1144
paulocsanz wants to merge 1 commit into
masterfrom
pcs/mysql-pitr-cli

Conversation

@paulocsanz

@paulocsanz paulocsanz commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Stacked: 2 of 2, on top of #1143 — review that one first. Base is pcs/mysql-redis-ha-cli, so the diff here is only the PITR delta (4 files, ~180 lines).

What this adds

railway mysql pitr — binlog archiving into a Railway bucket, enabled by the mysql-pitr composable overlay.

The generic pitr tree (from #1143) already drives everything an engine's archive needs from its declared contract, so this is mostly the declaration plus the two ways MySQL genuinely differs from Postgres. Both become declarations rather than branches on the engine name:

supports_ha: false — the image's archiver refuses to run whenever the Group Replication seed list is set, so MySQL PITR is standalone-only. Every path that would take or follow the rolling HA workflow checks this first:

  • progress / cancel / clear refuse before any network call
  • enable / disable refuse before the HA mutation

so you get one clear sentence instead of a server error from a workflow that was never going to start:

$ railway mysql pitr progress
MySQL PITR is standalone-only: its archiver does not run on HA cluster
members, so there is no rolling PITR workflow progress to show.

probe_kind: None — the live coverage probe in pitr status is pgBackRest shelling into the container, which MySQL has no equivalent tool for. Selecting the probe by declared kind means MySQL renders no coverage section at all, rather than a probe run with another engine's tooling or a fake "unavailable" for something it simply doesn't implement.

What needed no code

  • The archive variable contract. BINLOG_ARCHIVE_ as the declared prefix is all the enabled-state detection and the enable overlay need.
  • Image eligibility. It ships with the mysql-pitr template record. Worth noting that template does not set requireFloatingMajorTag, unlike postgres-pitr — its image matrix only publishes exact minors, so the old hardcoded "minor pins are bad" rule would have refused every MySQL image. Reading the declaration is what makes both engines correct at once.
  • Restore, backups and schedules. These ride the volume-instance mutations, which are engine-agnostic server-side.

Verification

  • cargo test — 1305 pass. New coverage for the per-engine archive-variable contrast (a Postgres service reads as unconfigured under MySQL's contract and vice versa), the HA-workflow gate refusing MySQL while passing Postgres, and probe-kind selection.
  • cargo clippy / cargo fmt clean.
  • The mysql-pitr template record was read from the live API; the fixtures mirror it.

@paulocsanz

Copy link
Copy Markdown
Collaborator Author

Note on the thin check list: ci.yml triggers only on pull_request: branches: [master], so a PR based on a feature branch runs label-check and nothing else. The full suite will run here once #1143 merges and GitHub retargets this to master.

Verified locally on this exact commit in the meantime:

cargo test    1286 passed; 0 failed
cargo clippy  no new warnings in any touched file
cargo fmt     clean

@paulocsanz
paulocsanz force-pushed the pcs/mysql-redis-ha-cli branch from 8e7a9f7 to 859f13e Compare August 27, 2026 20:29
@paulocsanz
paulocsanz force-pushed the pcs/mysql-redis-ha-cli branch from 859f13e to fd5ee15 Compare August 31, 2026 19:31
Base automatically changed from pcs/mysql-redis-ha-cli to master August 31, 2026 20:20
MySQL PITR is binlog archiving into a Railway bucket, enabled by the
`mysql-pitr` composable overlay. The generic `pitr` tree already drives
everything an engine's archive needs from its declared contract, so this is
mostly the declaration plus the two ways MySQL genuinely differs from Postgres.

Both differences become declarations rather than branches on the engine name:

  - `supports_ha: false`. The image's archiver refuses to run whenever the
    Group Replication seed list is set, so MySQL PITR is standalone-only.
    Every path that would take (or follow) the rolling HA workflow now checks
    this first -- `progress`/`cancel`/`clear` refuse before any network call,
    and `enable`/`disable` refuse before the HA mutation -- so the user gets
    one clear sentence instead of a server error from a workflow that was
    never going to start.
  - `probe_kind: None`. The live coverage probe in `pitr status` is pgBackRest
    shelling into the container, which MySQL has no equivalent tool for.
    Selecting the probe by declared kind means MySQL renders no coverage
    section at all, rather than a probe run with another engine's tooling or a
    fake "unavailable" for something it simply does not implement.

The archive variable contract needs no code: `BINLOG_ARCHIVE_` as the declared
prefix is enough for enabled-state detection and the enable overlay, and the
image-eligibility rules ship with the `mysql-pitr` template record. Notably
that template does NOT require a floating major tag, unlike postgres-pitr --
its image matrix only publishes exact minors, so a hardcoded "minor pins are
bad" rule would have refused every MySQL image.

Restore, backups and schedules ride the volume-instance mutations, which are
engine-agnostic server-side and needed no change.
@paulocsanz

Copy link
Copy Markdown
Collaborator Author

Rebased onto v5.47.0 (c258497, replacing 179ddfa): the old base branch pcs/mysql-redis-ha-cli predated #1149's commit-staged wait and #1159's five fixes, both now on master. The replay was conflict-free and the PR diff now shows only the MySQL PITR commit. cargo test 1312 pass, clippy/fmt clean on the touched files.

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

Labels

release/minor Author minor release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant