feat: railway postgres pitr/ha/pgbouncer — CLI parity for Postgres plugin management - #1032
Draft
paulocsanz wants to merge 8 commits into
Draft
feat: railway postgres pitr/ha/pgbouncer — CLI parity for Postgres plugin management#1032paulocsanz wants to merge 8 commits into
paulocsanz wants to merge 8 commits into
Conversation
added 8 commits
July 28, 2026 15:32
src/gql/schema.json was stale relative to the live backend (missing templateRevert, enablePitrForHaCluster/disablePitrForHaCluster, pitrHaWorkflowProgress, pitrHaClusterReplicationHealth, groupSet, and dozens more mutations/queries needed to build Postgres PITR/HA/PgBouncer CLI support). Regenerated by converting the checked-in backboard SDL to graphql_client's introspection-JSON shape instead of live-introspecting prod, so it's reproducible offline and stays in sync with mono at build time. Picks up one real schema change along the way: SandboxCreateInput grew a required `region` field since the snapshot was last refreshed.
…nd tree
Phase A of Postgres plugin CLI parity: shared infrastructure, all GraphQL
documents the feature will ever need, and the full command surface with
real handlers for the thin template-apply/revert wrappers (enable, disable,
convert, revert, add, remove, cancel, clear) and config-derived status.
Feature-specific live-probe and backup/schedule verbs are stubbed with
real flag parsing and a `not yet implemented` body, to be filled in by a
later phase without touching the shared GraphQL registration files again.
- ServiceInstance.cluster_role field (environment.rs)
- controllers/postgres_plugins.rs: PITR/HA/PgBouncer topology + guardrail detection
- controllers/template_apply.rs: templateDeployV2/templateRevert wrapper,
porting the frontend's replica/coordinator/edge count adjustment and
edge-variable injection logic against the serializedConfig JSON, plus an
auto-deploy-by-default (--no-deploy to only stage) commit wrapper
- controllers/exec.rs: promoted from db_stats' private exec_command_in_container
- commands/postgres/{mod,pitr,ha,pgbouncer}.rs: full command tree
- 17 new mutation + 5 new query GraphQL documents
Finishes the `railway postgres pgbouncer` stubs left by the Phase A scaffold: - `configure`: edits POOL_MODE/MAX_CLIENT_CONN/DEFAULT_POOL_SIZE/ MAX_PREPARED_STATEMENTS on the resolved edge service via the shared stage+commit patch mechanism (`--no-deploy` to only stage), printing the same advisory (non-blocking) misconfiguration warnings the frontend's PoolSizingRows computes before applying the change. - `scale --replicas N`: delegates into the existing multi-region replica-patch controller (`build_multi_region_patch`/`merge_config`/ `validate_total_replicas`) scoped to the PgBouncer edge service, resolving its single currently-deployed region rather than reimplementing region handling. - `status`: adds a live `SHOW POOLS`/`SHOW STATS`/`SHOW SERVERS` probe over the existing SSH exec primitive, with the same utilization thresholds (0.8/0.9/0.95) as the frontend; degrades to "probe unavailable" under a 5s timeout instead of failing the command. Promotes `template_apply::commit_staged_patch`'s stage step into a new `stage_and_commit_patch` helper, and bumps `db_stats`'s section/int parsing helpers to `pub(crate)` so the live probe can reuse them instead of duplicating the parsing convention.
… + live status probe
Wires the remaining `railway postgres pitr` stubs left by the phase-A scaffold:
progress (with --watch polling), restore (point-in-time into a new service),
backup {list,create,delete,lock,restore,trigger}, and schedule {set,list}.
Also adds a best-effort live coverage/archiver probe to `status`, SSHing into
the root service's container to run `pgbackrest info` and a `pg_stat_archiver`
query, gated behind a 5s timeout and degrading gracefully when unreachable.
`backup trigger` has no dashboard equivalent -- flagged as such in --help.
…e status probe, revert precheck
Completes the largest remaining piece of `railway postgres ha`:
- `scale --replicas/--coordinators/--edge`: new `controllers/cluster_scale.rs`
mutates an already-converted HA cluster's live topology, cloning an
existing sibling replica/coordinator's shape (source, variables, volume)
to scale up, deleting the highest-numbered replica or the lowest-numbered
("primary") coordinator to scale down, and restamping the cluster's
declared wiring (identity vars, data-node list, coordinator hosts,
quorum) against `EnvironmentConfig` -- re-derived from
`template_apply.rs`'s conversion-time wiring logic, not force-converted
from its serde_json::Value shape. Edge scaling delegates to the same
multi-region-config patching concept `scale.rs`/`regions.rs` already use.
Coordinator counts are validated odd up front. New service/volume
create+delete happen via direct mutations; the wiring/variable diff is
staged and committed through the same `EnvironmentPatchCommitStaged`
auto-deploy toggle `convert`/`revert` already use.
- `switchover --to <service>` (alias `promote`): new `controllers/patroni.rs`
exec-probes Patroni's REST API in any reachable cluster member's
container to find the current leader, then POSTs `/switchover` to
promote the candidate. No GraphQL mutation exists for this -- it's the
CLI's own primitive, mirroring what the frontend does over its browser
tunnel.
- `status` now shows a live per-member role/state/lag column (best-effort,
5s timeout per member, degrades to "unreachable" instead of failing).
- `revert` now prechecks that the root is the current Patroni leader before
allowing the revert, erroring with a pointer to `switchover` if not
(matches the frontend's own gate); degrades to a warning if no member is
reachable at all, mirroring `pitr disable`'s replication-health check.
Adds `ClusterWiring`/`group_id` fields to `ServiceInstance` (the
`environment.config` shape) and promotes `template_apply::commit_staged_patch`/
`format_data_node_entry`/`private_domain_ref` to `pub(crate)` for reuse.
# Conflicts: # src/controllers/template_apply.rs
Inline the three clippy::print_literal hits in ha.rs/pitr.rs's table headers, and drop the now-fully-unused not_yet_implemented helper now that every phase-B verb has a real implementation.
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.
Summary
Adds
railway postgres {pitr,ha,pgbouncer}— CLI parity for the three biggest Postgres-plugin features, which today are frontend-only: point-in-time recovery / continuous backups, high-availability clustering, and PgBouncer connection pooling.railway postgres pitr—status,enable/disable(standalone via template overlay, or HA-aware via dedicatedenablePitrForHaCluster/disablePitrForHaCluster+progress/cancel/clear),restore --at <time>(point-in-time restore to a new service),backup {list,create,delete,lock,restore,trigger},schedule {set,list}.railway postgres ha—status(topology + live Patroni role/state/lag),convert/revert,scale --replicas/--coordinators/--edge(creates/deletes cluster member services and restamps consensus wiring),switchover/promote.railway postgres pgbouncer—status(config + liveSHOW POOLS/STATS/SERVERS),add/remove,configure(pool mode/knobs with advisory warnings),scale.All enable/disable/convert/revert/add/remove actions auto-deploy by default (
--no-deployto only stage, matching the underlyingtemplateDeployV2/templateRevertmechanism's staging behavior). Destructive actions confirm interactively, with--yesfor automation.Notable pieces
src/gql/schema.jsonwas significantly stale relative to the live backend — regenerated from the current backboard SDL (offline conversion, not live introspection) as a prerequisite. Picked up one unrelated real schema change along the way (SandboxCreateInput.region).src/controllers/template_apply.rsports the frontend's template-config adjustment logic (useApplyComposableTemplate.tsx) so replica/coordinator/edge counts and edge-variable overrides are actually respected on enable/convert, not just the template's authored defaults.src/controllers/cluster_scale.rs/src/controllers/patroni.rsare net-new: live cluster member create/delete + wiring restamp, and Patroni REST calls over the existing SSH exec-in-container primitive (promoted fromdb_statsintosrc/controllers/exec.rs) since there's no GraphQL mutation for switchover or live node status./cluster, PgBouncerSHOW POOLS,pgbackrest info) are all best-effort with short timeouts —statusstill prints config-only data if a probe is unreachable.Test plan
cargo buildcleancargo test --bin railway— 607 passed, 0 failedcargo clippy --bin railway -- -D warnings— clean on every file this PR touches (confirmed the remaining pre-existing findings are in files untouched by this diff)cargo fmt --checkclean--helprenders correctly at every level (postgres,postgres {pitr,ha,pgbouncer}, and leaf subcommands)