Repository navigation
fix(local): warm dev restarts dead databases, and --fresh actually wipes them - #307
kristof-siket wants to merge 10 commits into
Conversation
…just the record The postgres daemon could only delete an app's persisted PGlite data with a prismaDevModulePath it had seen on a PUT in its own lifetime. A daemon restarted since the app's last PUT (and a warm dev never re-PUTs an unchanged Database) dropped the app's records but kept the data, and the next start under the same instance name reopened it: FRICTION #16, where --fresh minted a new auth secret over the old auth.jwks key and every session 500'd. DELETE now takes the path in an optional JSON body, falls back to the one a PUT carried, and fails with a 500 when it has neither, rather than drop a record whose data stays on disk. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Kristof Siket <siket@prisma.io>
…ows a failed wipe Teardown swallowed every error from every daemon, so a DELETE that failed still reported a clean --fresh. And it ran before the emulators hook, so a postgres daemon that wasn't running (after a reboot, say) was skipped and its persisted apps.json record and data survived --fresh. Dev now ensures the emulators before --fresh teardown. Teardown only skips a daemon that isn't running, sends the app's @prisma/dev path with the postgres DELETE, and lets a running daemon's failure surface as DEV.TEARDOWN_FAILED. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Kristof Siket <siket@prisma.io>
The Database reconcile's PUT is the only thing that starts a local database server, and the postgres daemon holds its servers in-process: a restart (version-skew replacement, a crash, another app's dev) drops them all. With unchanged props Alchemy's default diff is noop, so a warm dev never re-PUT, reported ready, and services got ECONNREFUSED on the recorded ports (FRICTION #15). It is the Postgres twin of the Compute gap startServices closed (ADR-0041). The provider's diff is now always update. It declares every attribute stable while the daemon still records the same URL (the port is pinned, so the PUT can't move it), so consumers keep noop-ing and a warm start only re-PUTs the databases. If the URL moved, the attributes are unknown until reconcile and the Connection and env rows reconverge through Alchemy's own diff. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Kristof Siket <siket@prisma.io>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Kristof Siket <siket@prisma.io>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Kristof Siket <siket@prisma.io>
|
✅ Gizmo reviewed bacb05a — posted 0 inline comment(s) this pass. Open findings: none Change walkthroughThis PR fixes two local-dev failures that share one root cause: the machine-global postgres daemon runs its Local Database provider — postgres.ts:64-90 adds the local-target providers' first custom Daemon API — postgres-main.ts:841-850 gives Teardown — teardown.ts:18-52 sends Composer's pinned CLI pipeline — execute-dev.ts:89-98 moves the emulators hook before Docs and tests — a design note beside the existing |
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueComment |
commit: |
…s-warm-restart-and-fresh
…h delete main made resolvePrismaDevModulePath() argument-free (it resolves the @prisma/dev Composer pins, not the app's). Teardown follows. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Kristof Siket <siket@prisma.io>
…docs Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Kristof Siket <siket@prisma.io>
…, like PUT
Replace the optional DELETE body, the per-daemon-lifetime path fallback and
the 'refuse without a path' branch with one contract: DELETE carries
{ prismaDevModulePath } exactly as PUT does, parsed by the same helper. The
fallback map was the bug: a restarted daemon had no entry. Teardown passes
Composer's own @prisma/dev path. Also drops an unreachable name check from
the Database diff and shortens the design note.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Kristof Siket <siket@prisma.io>
Revert the DELETE-body approach: deleteApp(app) is unchanged. The daemon deleted persisted data only with a path remembered in memory from a PUT, so a restarted daemon kept the data. It now resolves the @prisma/dev it ships with (a dependency of @prisma/composer-prisma-cloud since the version pin), the same way local-target resolves it, so no caller supplies a path and state written by older daemons is wiped too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Kristof Siket <siket@prisma.io>
fix(local): warm
devrestarts dead databases, and--freshactually wipes themFixes two local-dev bugs found while building an app on 0.20.0 (three Postgres resources plus
auth()andemail()). They share one root cause.Root cause
The postgres emulator daemon runs its
@prisma/devservers inside its own process. It is machine-wide, so another app'sdevcan restart or replace it (version skew, a crash, a reboot), and every server it held goes with it. The only thing that starts a server again is the localDatabaseprovider's reconcile (aPUT). On a warm start the props haven't changed, so Alchemy's default diff isnoopand thePUTnever happens.devstill reports ready. On the reporting machine the daemon had been restarted by another project (wedding-website). The "new ports" thatlsofshowed (:51314/:51319/:51326) were that app's database. None of daily-journal's databases were running. This is the same gap ADR-0041 records and fixes for Compute withstartServices(feat(cli): prisma-composer dev — bring the whole app up locally, credential-free #164). Postgres never got that fix.--freshkeeps the auth DB.DELETE /apps/<app>could only delete PGlite data using aprismaDevModulePaththe daemon had seen on aPUTin its own lifetime. Warm starts neverPUT, so a restarted daemon had no path. It dropped the app's records but kept the data, and the next start under the same instance name reopened the old data under a newly generated auth secret. The result:Failed to decrypt private keyon every session. Separately, teardown ran before the emulators hook and swallowed every error. So a daemon that was down at--freshtime was skipped completely, and a failed wipe still reported success.Changes
LocalDatabaseProvider.diffalways returnsupdate, so every converge re-PUTs. ThePUTis idempotent for a live server and restarts a dropped one on its pinned port.stables. Consumers then keep noop-ing, and a warm start only reconciles the Database resources, not migrations or Deployments.DELETEresolves Composer's own@prisma/dev(a dependency of@prisma/composer-prisma-cloud, which ships the daemon), the same way local-target does. It no longer depends on a path remembered in memory from aPUT. ThedeleteApp(app)interface is unchanged, and state written by older daemons is wiped too.dev-emulatorsnow lists@prisma/devunderdependencies.--freshteardown skips only daemons that aren't running, and lets a running daemon's failure surface asDEV.TEARDOWN_FAILEDinstead of being swallowed.executeDevensures the emulators before--freshteardown (it used to be after preflight), so a daemon that was down is running and can be wiped. This matches step 4 of the local-dev spec ("Containers + emulators").docs/design/10-domains/local-dev.md, next to the existingstartServicesgap. The user guides already promise "warm start: same ports, same data" and "--freshwipes data", so they don't change.Verification
End-to-end against
examples/auth(2 Postgres DBs +auth()+email()), under an isolatedHOMEso it had its own daemons:noop, "ready", nothing on :51300/:51304, auth logsECONNREFUSED 127.0.0.1:51300, sign-up 500database-db/mail.db-dbreconcile; no migrate, warm, env or deploy churnCOMPOSER_AUTH_SERVICE_DB_URLand the auth/api/ops deployments reconverge, sign-up 200--freshwith the daemon down beforehandauth.user1 → 0, new generated secret--freshover state written by the previous daemon buildRegression tests:
dev-emulators/postgres.test.ts: DELETE after a daemon restart, with no PUT in between, deletes the data. This failed on the old daemon: the table was still there after the wipe.local-target/postgres-always-reconciles.test.ts: the diff is nevernoop, and is stable only while the URL is pinned.bun testpasses for the local-target suite, the postgres daemon suite, the target invariants andcli/src/operations. Typecheck and biome are clean for the touched packages.Not in this PR
--freshalso reallocates service ports (the app moved from :3021 to :3018). That's a separate issue:AUTH_BASE_URLpointing at a dynamically allocated port.🤖 Generated with Claude Code