feat(db): reset and pull verbs for the shared Postgres (spec 15) - #103
Merged
Conversation
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
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Graduates the last two spec-15 (
docs/specs/15-db-snapshot-restore.md) data-lifecycle stubs ininternal/cli/db.gointo real verbs, alongside the existingdb 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.
engineTarget→ ledger-allocated 127.0.0.1 port + loopbackcompose upoverlay), so the pgx admin connection reaches the warm server without publishing a permanent host port.pg_terminate_backend WHERE datname=<db>),DROP DATABASE IF EXISTS, then re-runs the idempotent provisioner (EnsureProject) to recreate an empty role+db.--yes(--jsonrequires--yes);--forceoverrides a still-connected DB.LogEvent("db.reset")run insidelock.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 reusesinternal/dbRestore+ the overlay, refusing to clobber a non-empty tenant (rundb resetfirst to re-seed).--help.Restore/Pullnow share areplaySnapshothelper parameterized by event kind, so a pull logs a distinctdb.pullevent.Why
Completes the thin-v2 Postgres data workflow:
snapshot/restore/reset/pullon the per-project tenant of the shared Postgres.How tested
internal/orchestrate/reset_test.go(mockdb.Runner+ mockprovision.Conn, no real Postgres):DROP DATABASE/CREATE ROLE/CREATE DATABASEargv order, re-recorded ledger role+db rows, thedb.resetevent, and the overlay applied viacompose up;--force, then proceeds (DROP runs) with--force;--jsonshape;db.pullevent) and refuses a non-empty tenant.internal/cli/db_reset_pull_test.go: command registration, flags, and the--json-requires---yesguard.Gates:
CGO_ENABLED=0 go build ./...,CGO_ENABLED=1 go test ./internal/...,gofmt -l(clean),go vet ./..., andmake determinismall green.🤖 Generated with Claude Code