feat(database): add MySQL HA, Redis HA and MySQL PITR commands - #1142
Closed
paulocsanz wants to merge 1 commit into
Closed
feat(database): add MySQL HA, Redis HA and MySQL PITR commands#1142paulocsanz wants to merge 1 commit into
paulocsanz wants to merge 1 commit into
Conversation
`railway postgres` shipped HA, PITR and pooling for one engine, with the
engine's specifics compiled in: `PATRONI_ENABLED` as the definition of a
cluster, `WAL_ARCHIVE_BUCKET` as the definition of an archive, and hardcoded
Postgres image predicates as the enable gate. MySQL and Redis now have the
same managed features in the product, and none of them fit those constants.
Generalize the tree instead of copying it. The subcommand bodies move to
`commands/database/` and take the engine as a parameter; `railway postgres`,
`railway mysql` and `railway redis` are thin capability declarations wired to
them, so `--help` only ever lists what an engine actually ships (no PITR or
pooling under `redis`, no pooling under `mysql`).
What a cluster is, and how it is driven, now comes from what the template
declares rather than from the CLI:
- `haActiveVariable` decides whether a root is a live cluster, so Sentinel
and Group Replication are recognized without naming either.
- `haConversionConfig` bounds `convert`: a role the companion does not
declare does not exist in that topology, so `--coordinators` is refused
for the colocated-coordinator engines instead of being ignored, and each
role's counts come from the companion's own selectors. Read from the
COMPANION record, which is what the server-side gate reads -- a standalone
service may carry a partial copy or (redis) none at all.
- `adoptionImageEligibility` replaces the hardcoded image guardrails. This
fixes a real divergence: the old rules refused any minor-pinned image,
which is every image mysql-ha/mysql publishes.
- `clusterWiring` selects the switchover mechanism. Postgres keeps the
Patroni client; Redis and MySQL use the declared per-node role/switchover
endpoints, which is the only transport their nodes expose.
Two live-scaling gaps this surfaced, both of which would have produced a
broken cluster:
- `peerHostsVariable` was never stamped, so a node added to a redis-ha or
mysql-ha cluster booted knowing no peers. Now stamped on joining nodes
only, per the platform contract -- restamping existing nodes would mark
the whole fleet stale for a change none of them needs.
- Nothing enforced the quorum shape where the data nodes are the voters, so
scaling to an even cluster was accepted and would fail its next failover.
Postgres behaviour is unchanged: same subcommands, same flags, same JSON
shapes, same `~/.railway/postgres-ops.jsonl` trail (each engine now keeps its
own). The PITR live-coverage probe stays pgBackRest-only, selected by the
engine's declared probe kind rather than skipped by name, so an engine
without one reports nothing instead of a fake "unavailable"; MySQL PITR
refuses the HA workflow verbs up front, since its archiver only runs
standalone.
Collaborator
Author
|
Superseded: split into two PRs so MySQL PITR lands on its own.
Closing this one. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
railway postgresshipped HA, PITR and connection pooling for one engine, with that engine's specifics compiled into the CLI:PATRONI_ENABLEDwas the definition of "this is a cluster"WAL_ARCHIVE_BUCKETwas the definition of "archiving is on"postgres-ssl/postgres-patronipredicates were the enable gateMySQL HA, Redis HA and MySQL PITR are all in the product now, and none of them fit those constants. Redis clusters key off
SENTINEL_ENABLED, MySQL offGR_ENABLED; MySQL's archive lives underBINLOG_ARCHIVE_*; neither has a coordinator tier.What this does
Generalizes the tree rather than copying it three times. The subcommand bodies move to
commands/database/and take the engine as a parameter.railway postgres,railway mysqlandrailway redisare thin capability declarations wired to them, so--helponly lists what an engine actually ships:hapitrpgbouncerrailway postgresrailway mysqlrailway redisredis pitrisn't a runtime refusal — the subcommand doesn't exist.Everything engine-specific is read, not compiled
The CLI no longer decides any of this; the template does, and it's read off the live config or the fetched template record:
haActiveVariabledecides whether a root is a live cluster, so Sentinel and Group Replication are recognized without either being named.haConversionConfigboundsconvert. A role the companion doesn't declare doesn't exist in that topology, so--coordinatorsis refused for the colocated-coordinator engines instead of silently ignored, and each role's allowed counts come from the companion's own selectors. Read from the companion record — the same one the server-side gate reads. (A standalone service may carry a partial copy of it, or, like theredistemplate, none at all, so trusting the service's copy would skip the check for exactly the engine that needs it.)adoptionImageEligibilityreplaces the hardcoded image guardrails. This fixes a real divergence: the old rules refused any minor-pinned image, which is every imagemysql-ha/mysqlpublishes.clusterWiringselects the switchover mechanism. Postgres keeps the Patroni client; Redis and MySQL use the declared per-node role/switchover endpoints, which is the only transport their nodes expose.Adding an engine is a registry entry plus a command file; shipping a new supported major, or rewiring a cluster, stays a template update with no CLI release.
Two live-scaling bugs this surfaced
Both would have produced a broken cluster, and both are fixed here:
peerHostsVariablewas never stamped. A node added to aredis-haormysql-hacluster booted knowing no peers. Now stamped on joining nodes only, per the platform contract — restamping existing nodes would mark the whole fleet stale for a change none of them needs to see.Postgres is unchanged
Same subcommands, same flags, same JSON shapes, same
~/.railway/postgres-ops.jsonltrail (each engine now keeps its own, so an existing trail stays where its owner left it). The pgBackRest coverage probe is untouched — it's now selected by the engine's declared probe kind rather than skipped by name, so an engine without one reports nothing instead of a fake "unavailable".Verification
cargo test— 1286 pass. New coverage for per-engine cluster/archive detection, the companion-declared role bounds, the peer-list stamping and quorum fence, the declared-HTTP probe contract, and the per-engine command surfaces.cargo clippy/cargo fmtclean (no new warnings in any touched file).postgres-ha,redis-ha,mysql-ha,postgres-pitr,mysql-pitr) rather than assumed, and the fixtures in the tests mirror what those records actually contain.Not covered here:
converton a bare-major Redis/MySQL image is refused (the cluster pins every node to the source's exactmajor.minor) with the retag as the remedy, but the CLI can't yet detect the running minor for you the way the dashboard does. Worth a follow-up.