docs(deployment): rewrite migration prose for goose adoption
Backend CI / Test (push) Has been cancelled
Backend CI / Contract Tests (push) Has been cancelled
Backend CI / Build (push) Has been cancelled
Backend CI / Lint (push) Has been cancelled
Backend CI / Secret Scanning (push) Has been cancelled

Update the deployment book and glossary to reflect the goose-based
schema migration flow shipped in 12b2f9d/0f7450a:

- ch07: clarify startup probe assumes migrations ran out-of-band
- ch08: drop AutoMigrate-with-advisory-lock prose; describe goose Job
- ch12: pod startup checks goose_db_version, no longer runs migrations
- ch14: document the Job→wait→roll deploy gate and how to debug failures
- ch16: add "Migrate Job fails during deploy" + "Schema precondition
  failed" failure modes
- ch17: new runbook entries §26 (run migrations manually), §27 (recover
  from failed/dirty migration), §28 (bootstrap goose on fresh clone)
- ch19: postscript on §13 noting MigrateWithLock approach is superseded
- ch20: mark "Migration Job for schema changes" task done
- glossary: add `goose` and `goose_db_version`; flag AutoMigrate as
  tests-only
- references: add goose links; flag AutoMigrate as tests-only
This commit is contained in:
Trey t
2026-04-26 23:01:32 -05:00
parent 0f7450ada9
commit 8d9ca2e6ed
10 changed files with 260 additions and 39 deletions
+88 -4
View File
@@ -428,10 +428,94 @@ KUBECONFIG=~/.kube/honeydue.yaml bash deploy-k3s/scripts/03-deploy.sh --skip-bui
```
The pooler runs in transaction mode so any session-scope feature
(LISTEN/NOTIFY, session advisory locks for migrations) auto-falls
through to direct via `MigrateWithLock` opening its own connection.
But if you ever add session-level features in the data path, they'll
need the direct endpoint.
(LISTEN/NOTIFY, session advisory locks) won't work over it. Migrations
already handle this — the migrate Job script strips `-pooler` from
`DB_HOST` before invoking goose. If you add new session-level features
in the data path, they'll need the same workaround.
## 26. Run migrations manually (rare)
Day-to-day, migrations run as part of every `03-deploy.sh`. But
sometimes you want to apply or inspect them outside a deploy:
```bash
# Direct-endpoint DSN (goose's advisory lock won't survive the pooler)
DB_PASS=$(kubectl -n honeydue get secret honeydue-secrets \
-o jsonpath='{.data.POSTGRES_PASSWORD}' | base64 -d)
export DATABASE_URL="host=ep-floral-truth-amttbc5a.c-5.us-east-1.aws.neon.tech \
port=5432 user=neondb_owner password=$DB_PASS \
dbname=honeyDue sslmode=require"
# What's pending? (read-only; safe to run anytime)
make migrate-status
# Apply pending migrations (or `goose -dir migrations postgres "$DATABASE_URL" up`)
make migrate-up
# Roll back the most recent migration
make migrate-down
# Scaffold a new migration file
make migrate-new name=add_widget_count_to_residences
# → migrations/000002_add_widget_count_to_residences.sql
# Edit, then `make migrate-up` to test, then commit.
```
To run goose from inside the cluster (e.g., to bypass a network policy
that blocks Neon from your laptop), use the migrate Job manifest as a
one-shot:
```bash
# Re-runs the latest migrate Job with whatever args you need
kubectl -n honeydue delete job honeydue-migrate --ignore-not-found
sed "s|image: IMAGE_PLACEHOLDER|image: $(kubectl -n honeydue get deploy api -o jsonpath='{.spec.template.spec.containers[0].image}')|" \
deploy-k3s/manifests/migrate/job.yaml | kubectl apply -f -
kubectl -n honeydue wait --for=condition=complete --timeout=5m job/honeydue-migrate
kubectl -n honeydue logs job/honeydue-migrate
```
## 27. Recover from a failed/dirty migration
If `goose up` fails partway through, the migration file's transaction
rolls back and `goose_db_version` reflects the last *complete*
version. Goose marks no row as "dirty" — that's a golang-migrate
concept. So recovery is just: fix the migration file, re-run.
If you've genuinely corrupted state (dropped tables you shouldn't have,
applied a destructive migration in error):
```bash
# See current goose state
make migrate-status
psql "$DATABASE_URL" -c \
"SELECT version_id, is_applied, tstamp FROM goose_db_version ORDER BY id DESC LIMIT 10;"
# To force the version table back to a known-good number after
# manually fixing the schema:
psql "$DATABASE_URL" -c \
"INSERT INTO goose_db_version (version_id, is_applied, tstamp) VALUES (<N>, true, NOW());"
```
## 28. Bootstrap goose on a fresh clone of the schema
If you create a new Neon branch / dev DB and need to bring it under
goose management:
```bash
export DATABASE_URL="...<the new DB>..."
# Option A: fresh DB, no schema → just run up
make migrate-up
# Option B: schema already populated (e.g., restored from a dump) →
# mark v1 as already-applied
goose -dir migrations postgres "$DATABASE_URL" version # creates table
psql "$DATABASE_URL" -c \
"INSERT INTO goose_db_version (version_id, is_applied, tstamp) VALUES (1, true, NOW());"
```
This is also what was done for the live prod DB at goose-adoption time
(commit `12b2f9d`).
## References