Skip to content

feat(db): reset and pull verbs for the shared Postgres (spec 15) - #103

Merged
gustavobertoi merged 1 commit into
mainfrom
feat/db-reset-pull
Jul 1, 2026
Merged

feat(db): reset and pull verbs for the shared Postgres (spec 15)#103
gustavobertoi merged 1 commit into
mainfrom
feat/db-reset-pull

Conversation

@gustavobertoi

Copy link
Copy Markdown
Contributor

What

Graduates the last two spec-15 (docs/specs/15-db-snapshot-restore.md) data-lifecycle stubs in internal/cli/db.go into real verbs, alongside the existing db snapshot/db restore.

db reset [--project P] [--yes] [--force]

DROP and re-provision a project's per-project tenant database on the shared Postgres to a clean, empty tenant.

  • Reuses the provision phase's exact host-reachability path (engineTarget → ledger-allocated 127.0.0.1 port + loopback compose up overlay), so the pgx admin connection reaches the warm server without publishing a permanent host port.
  • Terminates only this tenant's live backends (pg_terminate_backend WHERE datname=<db>), DROP DATABASE IF EXISTS, then re-runs the idempotent provisioner (EnsureProject) to recreate an empty role+db.
  • Destructive: refuses without --yes (--json requires --yes); --force overrides a still-connected DB.
  • The terminate → DROP → re-provision DDL + ledger writes + LogEvent("db.reset") run inside lock.WithLock. Only the tenant DATABASE is dropped — the shared container/volume is never bounced (never-recreate-a-stateful-shared-service guard holds).

db pull <name> [--project P]

Seed a project's tenant from a named snapshot in the local store ($DEVSTACK_HOME/snapshots) — a fetch-by-name + restore that reuses internal/db Restore + the overlay, refusing to clobber a non-empty tenant (run db reset first to re-seed).

  • The remote/team "shared store" (S3/HTTP fetch + the mandatory sanitize transform) is DEFERRED to spec 21 and clearly noted in code + --help.
  • Restore/Pull now share a replaySnapshot helper parameterized by event kind, so a pull logs a distinct db.pull event.

Why

Completes the thin-v2 Postgres data workflow: snapshot/restore/reset/pull on the per-project tenant of the shared Postgres.

How tested

internal/orchestrate/reset_test.go (mock db.Runner + mock provision.Conn, no real Postgres):

  • reset drops+reprovisions — asserts terminate/DROP DATABASE/CREATE ROLE/CREATE DATABASE argv order, re-recorded ledger role+db rows, the db.reset event, and the overlay applied via compose up;
  • reset refuses a still-connected DB without --force, then proceeds (DROP runs) with --force;
  • reset --json shape;
  • pull restores a named snapshot (distinct db.pull event) and refuses a non-empty tenant.

internal/cli/db_reset_pull_test.go: command registration, flags, and the --json-requires---yes guard.

Gates: CGO_ENABLED=0 go build ./..., CGO_ENABLED=1 go test ./internal/..., gofmt -l (clean), go vet ./..., and make determinism all green.

🤖 Generated with Claude Code

Graduate the last two spec-15 data-lifecycle stubs.

`db reset [--project P] [--yes] [--force]`: DROP + re-provision a project's
per-project tenant database on the shared Postgres to a clean, empty tenant.
Reuses the provision phase's host-port overlay (engineTarget → ledger port +
loopback compose overlay) to reach the warm server, terminates only this
tenant's live backends, DROP DATABASE, then re-runs the idempotent provisioner
(EnsureProject) to recreate an empty role+db. Destructive: refuses without
--yes (and --json requires --yes); --force overrides a still-connected DB. The
terminate → DROP → re-provision DDL + ledger writes + LogEvent("db.reset") run
inside lock.WithLock. Only the tenant DATABASE is dropped — the shared
container/volume is never bounced (never-recreate guard holds).

`db pull <name> [--project P]`: seed a project's tenant from a named snapshot in
the LOCAL store ($DEVSTACK_HOME/snapshots) — a fetch-by-name + restore that
reuses internal/db Restore + the overlay, refusing to clobber a non-empty
tenant. The remote/team "shared store" (S3/HTTP fetch + the mandatory sanitize
transform) is DEFERRED to spec 21 and clearly noted as such.

Removes only the reset + pull stub lines from db.go.

Tests (mock runner + mock provisioner, no real Postgres): reset drops +
reprovisions (asserts terminate/DROP/CREATE argv order + ledger role+db rows +
db.reset event + overlay applied), reset refuses a still-connected DB without
--force then proceeds with --force, reset --json shape, pull restores a named
snapshot (distinct db.pull event), pull refuses a non-empty tenant, plus CLI
registration/flags + the --json-requires-yes guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gustavobertoi
gustavobertoi merged commit fe3d087 into main Jul 1, 2026
4 checks passed
@gustavobertoi
gustavobertoi deleted the feat/db-reset-pull branch July 1, 2026 15:51
gustavobertoi added a commit that referenced this pull request Jul 1, 2026
#103 (db reset/pull) and #105 (redis/minio dumpers) each passed CI alone but
broke `main` when both merged — a semantic conflict git didn't flag:

- #105 changed `resolveTenant` to take a `kind` and return `engine,label`
  (5 args / 6 returns); #103's `reset.go` still called the 4-arg form.
- #105 replaced the `defaultPgDumper()` helper with `SelectDumper(d, kind)`;
  #103's `db pull` still called the removed helper.

Both call sites now use the merged signatures (pg kind, extra returns discarded).
Build, `go vet`, full `go test ./internal/...`, and `make determinism` all green.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant