Files
moments/crates
rob thijssen 6ee4cf5299
All checks were successful
deploy / Build api + worker (static musl) (push) Successful in 5m33s
deploy / Deploy moments-worker to frootmig (push) Successful in 17s
deploy / Deploy moments-api to nikola (push) Successful in 19s
deploy / Build prerendered web (push) Successful in 4m0s
deploy / Deploy web to oolon (push) Successful in 20s
feat(api): make healthz verify the schema, not just that the process is up
`/v1/healthz` returned a static "ok" without touching the database, so
the deploy's health probe passed regardless of whether the schema the
binary expects had been migrated. An api newer than its schema sailed
through the probe and then failed one query at a time on whatever column
was missing — the exact failure that job ordering now prevents, with
nothing to catch it if that assumption ever breaks again.

It now compares the applied migration version against
`moments_data::expected_schema_version()`, derived from the migrations
compiled into this binary via the same `sqlx::migrate!` MIGRATOR that
applies them. There is no second list of expected columns to drift from
the real one.

  schema >= expected  -> 200 "ok (schema 6)"
  schema <  expected  -> 503, error-level journal line naming both versions
  no migrations       -> 503
  cannot read table   -> 200 "degraded: schema unverified (...)"

A newer schema than expected stays healthy: that is an api rollback under
a migrated database, and this binary's queries are still satisfiable. The
reverse is not.

The degraded case exists because `_sqlx_migrations` is created by
moments_rw and reaches moments_ro through the default privileges in
asset/sql/bootstrap-moments.sql. A role provisioned before those grants
would get 42501, and failing the probe over that would take a working api
offline for a permissions detail. `StoreError` gains an `Inaccessible`
variant so the two are told apart by SQLSTATE (42501, 42P01) rather than
by sniffing message text, and the condition is loud in both the journal
and the probe output.

Also corrected the startup comment in moments-api: it claimed the
api/worker ordering came from systemd dependencies, which cannot be true
across two hosts. It comes from deploy.yml.

Verified against postgres 16 with the production role split replicated
(moments_rw owning the schema, moments_ro granted through
bootstrap-moments.sql, plus a legacy role without those grants) and the
migrations applied by the real worker binary: current schema -> 200 "ok
(schema 6)"; version 6 row deleted -> 503 "schema at 5, this binary
expects 6"; table emptied -> 503 "no migrations applied"; legacy role ->
200 degraded, with `curl -fsS` exiting 0 and printing the reason. First
confirmed that moments_ro can in fact read `_sqlx_migrations` under the
documented grants, so the normal path is the precise one.

Refs #8
2026-08-17 13:10:10 +03:00
..