Postgres in place: a database per stack, migrations, backup and Drizzle Gateway #354
Labels
No labels
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
spec
wayfinder:grilling
wayfinder:map
wayfinder:prototype
wayfinder:research
wayfinder:task
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
amaurycoudr/chess-project#354
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem Statement
Nothing the app knows outlives a redeploy: Rooms live in memory (ADR 0029), and no stack has a database. Every account feature on the map needs one, starting with Guest history, which stores decided Online games. Production and up to five previews share one VPS, so a database must fit the deploy scripts as they are, keep preview data apart from production, survive the VPS being lost, and be inspectable and editable by the owner without exposing a port. Contributors, and the agents working in parallel worktrees, need the same database locally without stepping on each other.
Solution
Every stack (production and each preview) gets its own Postgres 18 container, private to that stack, its data in a volume the existing preview teardown already removes (ADR 0032). A one-shot migrate step applies the committed Drizzle migrations (ADR 0031) before the API container is replaced; a failed migration fails the deploy and leaves the old API serving. The API refuses to start without a reachable database. Production is dumped nightly, checked by restoring it, encrypted, and kept off the VPS. A self-hosted Drizzle Gateway on the VPS joins every stack's network and is reached only through an SSH tunnel; it edits production through a role that reads and writes rows but cannot change the schema. Locally, the API's tests run on an in-process Postgres; dev and the browser suite use a real Postgres container, one database per worktree.
This spec ships the tooling and an empty schema. No table: Guest history adds the first one. Nothing a Player sees changes.
User Stories
generatenever reaches a deploy.pnpm devand the local browser suite to run against a real Postgres, with one database per worktree, so that several worktrees run side by side without sharing data.Seams
loadConfig, unit tests beside the existing config tests: the database URL is required and parsed, and the password-file setting overrides the URL's password. Covers 10, 19.No new e2e test: a Player perceives nothing of this.
Implementation Decisions
Governing ADRs. ADR 0031 (Drizzle v1 RC pinned exactly, committed SQL migrations, the migrator in production, no repository abstraction) and ADR 0032 (a Postgres container per stack, no published port, a one-shot migrate step, one Gateway joining every stack, a no-DDL Gateway role) are accepted and govern this work. ADR 0029 stands. No new ADR is needed. One is worth writing if the owner agrees: "API tests run on an in-process Postgres, dev, CI and stacks on a real one", since every later spec inherits the choice.
Database per stack
pg_isreadyhealthcheck, and the shared-memory size the image's docs recommend.shared_buffers, about 20 connections and a container memory limit. Production keeps Postgres's defaults.Migrations
drizzle-ormanddrizzle-kitat the same exact RC (ADR 0031); production drivernode-postgres. drizzle-kit is a dev dependency.API wiring (within the API's existing layer order and framework-import rules, so no lint change)
Roles and secrets
Backup
ageto a public key committed in the repo (the owner alone holds the private key), and uploads it as an artifact kept 30 days. A failed dump, restore check or upload fails the run.Drizzle Gateway
drizzle-kit studiothrough the same tunnel, and say so on the map.Local dev, tests and CI
dbscript starts the container and creates the worktree's database if it is missing; the migrate step, when the database does not answer, names that script, as the missing-port error names the env sync.drizzle-kit studiostays available for local inspection.Owner setup. The production secrets, the backup key pair and the Gateway apply are steps only the owner can take; the implementer ships them as a
wizard.Proposed rule changes (for the PR body, not edited here): the API's coding standards describe its layout as having one feature module and name the wire-test fixture; both sentences should mention the database module and the in-process database.
Testing Decisions
loadConfig.Out of Scope
Further Notes
research/postgres-on-vpsbranch.to-tickets.Revised in place after ADR 0031 and ADR 0032 landed, and after data was split between the five account specs. What changed:
depends_on.wizard, and Out of Scope names what the later account specs own.mentioned in issue #367
mentioned in issue #368
mentioned in issue #369
mentioned in issue #370
mentioned in issue #371
mentioned in issue #373
mentioned in issue #355
marked this issue as related to #367
marked this issue as related to #368
marked this issue as related to #369
marked this issue as related to #370
marked this issue as related to #371
marked this issue as related to #355
mentioned in merge request !506
mentioned in commit
6ba8bbcfa7mentioned in merge request !507
First production backup run, by hand after merge (#371): pipeline 43, job
backup-productiongreen — https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/389chess-db-1: 4083 byteschess-db-2026-10-13T1416Z.dump.age, 4283 bytes, kept 30 daysNightly runs follow from schedule 2 (
41 2 * * *UTC,BACKUP=1). Production's first deploy (pipeline 41) passed its verify, including no preview caps.