Pre-deploy RDS snapshot gate for the unattended full updb + cim
Area: deployment / Ansible / RDS / database safety Raised during: Session 2026-08-17 (following up on the config-sync deploy fix) Jira: (add when available) Priority: Medium now, High before production rollout — deliberately not solving this during active development; must be resolved before this deploy path is trusted against production data.
What changed that makes this matter
deploy_backend.yml (terraform-infrastructure commit 5904a3684) now runs a full
drush updb + drush cim on every deploy that touches drupal/**/package/**/
pipeline/**, and fails the build on any error or remaining config drift. See
deploy-never-imports-config-sync.md. Before
this, neither command ran automatically, so there was no unattended, code-triggered
path that could mutate or destroy dev-0's database. Now there is: a bad update hook
or a config change with an unintended side effect (e.g. a field deletion) runs
unattended against the real database — currently the 111,340-node Images migration,
Group/collection membership data, and the user migration.
Decision B (2026-07-16, d11-dev-database-bootstrap-and-migration-source.md) already called for gating this on a fresh RDS snapshot; that half of the decision was never built. This note captures why it still matters and what was learned checking the live instance, so it doesn't need to be re-derived later.
What a naive "just take a snapshot" mitigation would miss
Checked live 2026-08-17 (aws rds describe-db-instances / describe-db-snapshots
against rds-mysql8-staging, staging aws-vault profile):
- There's already a baseline.
rds-mysql8-staginghas 7-day automated backups — both native RDS daily snapshots and AWS Backup jobs running roughly daily. This isn't a "zero backups" situation; a pre-deploy snapshot only needs to close the gap the daily cadence leaves. - Staleness is the real gap. A deploy can land any time between two daily
snapshots. If
updb/cimcorrupts something mid-day and nobody notices until the next day, the nearest automated snapshot could be up to ~24h stale relative to the moment right before the bad deploy. - Restore blast radius is bigger than it looks.
rds-mysql8-stagingis a shared 200GB instance, not mandala-dedicated — it also holdsmandala_d7_images,mandala_d7_shared, and likely other teams' staging databases. RDS snapshots are instance-level, not per-database. "Roll backmandala_drupal_0" via snapshot restore is not a clean rewind: it means restoring the whole instance to a new temporary instance, then extracting and re-importing justmandala_drupal_0by hand — the same manual dump-and-load mechanics as the D7 source refresh (scripts/refresh-d7-staging-source.sh), not a fast undo button. A pre-deploy snapshot narrows step 1 (staleness) but does not solve step 2 (extraction mechanics) — worth being honest about that gap in whatever gets built. - There's already a working precedent for a manual, purpose-tagged snapshot:
mandala-preusermigration-20260812-1151, taken by hand before the user migration. The proposed gate just automates that pattern per-deploy.
What has zero mitigation at all: Solr
A DB snapshot/restore — however well built — does not undo whatever a corrupted
deploy already pushed to the kmassets Solr index via KmassetDirectSink's
node-save hooks. Restoring the DB to a pre-deploy state while Solr keeps whatever
it was already sent leaves the two systems disagreeing with each other, and
nothing currently detects or fixes that divergence. This needs its own mitigation,
separate from the RDS snapshot — not scoped or designed yet.
What needs to happen (before production rollout, not now)
- Decide whether a pre-
updbsnapshot task belongs indeploy_backend.ymlitself (the routine per-deploy path, since that's what actually runsupdb/cimnow) rather thandeploy_install.ymlas Decision B's original text said —deploy_install.ymlis the one-time bootstrap playbook and doesn't run on routine deploys. - Decide on naming/retention convention for these snapshots (they're instance-level and shared storage — need a cleanup policy so they don't accumulate indefinitely on a shared 200GB instance).
- Decide whether the deploy should block waiting for the snapshot to reach
availablebefore proceeding (safer, slower every deploy) or proceed once the snapshot is requested (faster, but the point-in-time cut needs to be verified as reliable without waiting for full completion). - Design and document the actual restore runbook (temp-instance restore + single-DB extraction) — so it exists and is tested before it's ever needed under pressure, not written during an incident.
- Design a separate mitigation for the Solr-side divergence problem above — currently no plan exists.
Why this is fine to leave open right now
Deploys only fire on drupal/**/package/**/pipeline/** changes (docs-only
merges are filtered by trigger_paths), so actual updb/cim runs are
infrequent, and the project is still in active development — dev-0's data is
reproducible from migration, not irreplaceable production content. The urgency
changes materially once this deploy path (or its production equivalent) runs
against real production data.
Partial mitigation DECIDED and built (2026-08-25, Yuji): our own logical dumps
The instance-wide snapshots are not a usable routine safety net. RDS automated backups are per-INSTANCE, not per-database, and up to ~24h stale. Recovering one database from one means standing up a whole replacement instance — which nobody will realistically do mid-development, so in practice the net does not exist for the "undo the last hour" case, which is the case that actually keeps arising.
Decision: take our own logical dumps at defined checkpoints rather than relying
on the snapshot for routine work. Built as scripts/db-checkpoint.sh
(save / list / restore). It goes through drush, reusing the same $DRUSH
override scripts/migration-cycle.sh already takes, so it inherits the site's own
credentials and needs no MYSQL_* plumbing, no mysqldump client and no separate
secret. Restore is a targeted reload of one database, in minutes.
Suggested checkpoints around an acceptance run: pre-import, post-import,
post-validate.
This does not close this note. Scope of what is and is not covered:
- ✅ Covers the operator-initiated case — an acceptance cycle, a risky manual
cim, any "let me be able to undo this" moment. - ❌ Does not cover the case this note was raised for: the unattended,
code-triggered
updb/cimon deploy. Nothing invokes the checkpoint script fromdeploy_backend.yml, so a bad update hook still runs with no rollback point. Wiring a pre-deploy checkpoint into the playbook is the remaining work, and it needs a retention policy so deploys do not fill the volume. - ⚠ The checkpoint directory must sit on a persistent bind mount. If it does not, the checkpoints die with the container — the same failure mode the OAuth2 signing keys hit in August.
- ✅
saveandlistare now PROVEN on dev-0 (2026-08-25). Two verified checkpoints exist (pre-import-20260825T183428Z.sql.gz,post-deploy-20260825T193637Z.sql.gz, 75M each). Running it caught three real defects the earlier static checks had missed — see the script header. - ✅
restorePROVEN on dev-0, 2026-08-26, against a scratch schema (mandala_restore_test) on the real RDS with the real app user — same privileges, same network, only the target name differing. Restored 75M and matched the live database exactly: 286/286 tables, 111,341/111,341shanti_image, 166,395/166,395 paragraphs, 171/171 groups. Scratch schema dropped afterwards. A DDEV rehearsal was considered and rejected as insufficient: DDEV runs as root on a local MySQL, so it would have proven the drop/load logic while skipping the thing most likely to fail — whether the app user can actually DROP and reload on RDS. TheTARGET_DBoverride added for this makes the safety net testable without risking the live database.
RDS snapshots remain the disaster-recovery story; this covers what they are bad at.
Cross-references
- deploy-never-imports-config-sync.md — the fix that created this exposure
- d11-dev-database-bootstrap-and-migration-source.md — Decision B, the original (unbuilt) mitigation
- production-migration-planning.md — where this should get picked back up as rollout nears