Skip to content

fix(prisma-cloud): deploy services only after their database migration - #334

Merged
kristof-siket merged 3 commits into
mainfrom
fix/deploy-after-migration
Oct 2, 2026
Merged

kristof-siket merged 3 commits into
mainfrom
fix/deploy-after-migration

Conversation

@kristof-siket

@kristof-siket kristof-siket commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

What happens today

A service that uses a postgres() database can go live before that database's migration has finished. The deployment and the migration both depend only on the warm-up step, so they run in parallel.

Seen in a rehearsal on the preview platform on 2026-10-02 with @prisma/composer 0.25.0: the service's deployment was live at 11:03:58, and the migration finished at 11:07:33. For three and a half minutes, the new code ran against a database that was not yet at its target.

Why this is a bug

ADR-0022 says the deploy guarantees the live database is at the contract's hash, so the runtime binding does no schema check. The building-an-app guide has said "The deploy applies migrations/ before the service starts" since July. Nothing in the graph enforced that order. Before the warm-up step was added (d9f0db2), consumers got the plain connection url, so the edge to the migration never existed.

The cause and the fix

orm-postgres.ts created the migration and dropped its result, and published url: warm.url to consumers. Now url also references the whole migration resource and still resolves to the same connection string:

url: Output.map(Output.all(warm.url, Output.of(migration)), ([value]) => value)

This follows the pattern in deployment-edge.ts. The edge rides the environment row's value and the deployment's triggers, never artifactPath (ADR-0048).

  • Several databases: each url carries its own migration, so a service waits for all of its databases.
  • rawPostgres has no migration and is unchanged.
  • Local dev uses the same lowering, so the order holds there too.

What changes for users

  • A failed migration stops the new service code from shipping.
  • Deploys take longer, because services now deploy after the migration instead of alongside it.

The ADR-0022 amendment, the guide, and the prisma-composer-core-concepts skill say so.

A side effect, not documented as a guarantee: when a deploy changes the migration (a new contract or ref), the url is still unresolved at plan time, so upstream replaces the consuming deployments conservatively, even when their code is unchanged. core-model.md shows the new url.

ADR-0048 still calls the ordering helper dependsOnEnvironment. That was its name when the ADR was written; the code now calls it appAfterEnvironment. ADRs are append-only, so this PR leaves it.

Tests

  • New orm-postgres-ordering.test.ts drives the real descriptor and Output machinery. On main: 1 pass, 2 fail. With the fix: 3 pass.
  • @internal/prisma-cloud: 389 pass, 0 fail, 29 files (386 across 28 on main).
  • @internal/lowering: 198 pass.
  • tsc --noEmit clean for both packages; biome check clean on the changed files; lint:casts delta 0.

Not tested: the Postgres integration tests (no database was available) and an end-to-end deploy on the platform. A rehearsal should confirm the migration finishes before the deployment goes live.

🤖 Generated with Claude Code

kristof-siket and others added 2 commits October 2, 2026 12:02
A service that uses a postgres() database could go live before the
database's migration finished. The lowering published the warm-up's url
to consumers, and nothing a consumer references pointed at the migration,
so Alchemy ran the migration and the service deployment in parallel.

The url output now also references the whole migration resource. Each
consumer's environment row and deployment triggers carry that url, so the
deployment waits until the schema is at the target, and a failed
migration stops the new code from shipping. When the migration is a
no-op, the url resolves from state at plan time and adds no wait.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Kristof Siket <siket@prisma.io>
Amend ADR-0022 with the ordering rule its "no runtime schema check"
guarantee depends on, and note why the window where old code runs
against the new schema is safe. Update the core-model sketch of the
postgres lowering, and tell users in the guide and the skill what the
order means for them: a failed migration blocks the new code, and a new
migration target redeploys the services that use the database.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Kristof Siket <siket@prisma.io>
@prisma-gizmo

prisma-gizmo Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

✅ Gizmo reviewed 868ff7a — posted 0 inline comment(s) this pass.

Open findings: none

Change walkthrough

This PR makes a service that consumes a postgres() database deploy only after that database's migration completes, closing a gap where deployment and migration ran in parallel off the warm-up step. It does so by making the lowering's url output reference the whole OrmMigration resource (while still resolving to the connection string), so every consumer's environment row and deployment-trigger fingerprint wait on it; a failed migration then stops the new code from shipping.

Lowering — orm-postgres.ts now captures the previously discarded migration result and folds it into url via Output.map(Output.all(warm.url, Output.of(migration)), …), following the deployment-edge pattern: the ordering rides the environment row's value and the deployment's triggers, never artifactPath (ADR-0048). Each url carries its own migration, so a service with several databases waits for all of them; rawPostgres (no migration) is untouched, and local dev shares the lowering so the order holds there too.

Tests — the new orm-postgres-ordering.test.ts drives the real descriptor and Output machinery: it asserts the url's upstream set includes both warm-up and migration, that the url still resolves to the connection string rather than migration attributes, and that a consumer-side environment row built from the url keeps the migration as an upstream.

Docs & ADRs — the ADR-0022 amendment and the ADR index record the new ordering guarantee; core-model.md shows the new url shape. The building-an-app guide and the core-concepts skill state the user-visible contract in matching terms. Notably, the latest delta removed the earlier claim that a deploy moving the migration target redeploys consumers even with unchanged code — the resolved url value doesn't move in that case, so both docs now over-claim nothing and no other document still carries the dropped sentence. ADR-0048's dependsOnEnvironment name is left as-is per the append-only ADR rule, since the helper is now appAfterEnvironment.

@coderabbitai

coderabbitai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Essentials

Run ID: f21b0c5a-7777-481b-86a6-ac79b79f7322

📥 Commits

Reviewing files that changed from the base of the PR and between a1abb11 and 868ff7a.

📒 Files selected for processing (2)
  • docs/guides/building-an-app.md
  • skills/prisma-composer-core-concepts/SKILL.md

Included review availability: This review used your included allowance. 2 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 4 reviews per hour.


Summary by CodeRabbit

  • New Features
    • Services that use a database now deploy only after its migration completes. A failed migration prevents new code from shipping, and services are redeployed when a migration runs—even if their code is unchanged.
    • Existing code continues serving while the replacement deploys, so migrations must remain compatible with the code they replace.
  • Documentation
    • Updated deployment guidance to explain migration ordering and compatibility expectations.

Walkthrough

The PostgreSQL lowering retains the migration result and publishes its URL through an output that depends on both database warm-up and migration completion. Tests verify these dependencies, including for a Prisma environment-variable row. The design documentation, guide, and skill text describe deployment ordering, migration failure blocking new code, and compatibility between the migrated schema and code that remains active during deployment.

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to 868ff

The change describes migration-gated deployment without promising an unsupported redeployment behavior. It is mergeable after normal checks.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 2 files. (2 skipped: 2 …
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely states the main change: services deploy only after their Prisma Cloud database migration completes.
Description check ✅ Passed The description directly explains the ordering bug, the implementation change, user impact, documentation updates, and test results. It is clearly related to the changeset.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
✨ Simplify code
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Comment @coderabbitai help to get the list of available commands.

@pkg-pr-new

pkg-pr-new Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@prisma/composer@334
npm i https://pkg.pr.new/@prisma/composer-cli@334
npm i https://pkg.pr.new/@prisma/composer-prisma-cloud@334

commit: 868ff7a

@kristof-siket
kristof-siket marked this pull request as ready for review October 2, 2026 11:49

@prisma-gizmo prisma-gizmo Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New findings: 🟡 1 minor · trace

Comment thread docs/guides/building-an-app.md Outdated
The guide and the skill promised that moving the migration target
redeploys the consuming services. That happens today only because a
trigger member left unresolved at plan time replaces the deployment
conservatively; this change does not guarantee it. Keep the docs to
what the ordering edge enforces: services deploy after the migration,
and a failed migration stops the new code from shipping.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Kristof Siket <siket@prisma.io>

@prisma-gizmo prisma-gizmo Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New findings: none · trace

@prisma-gizmo prisma-gizmo Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All Gizmo review threads are resolved and the head commit has been reviewed. Approving.

@kristof-siket

Copy link
Copy Markdown
Contributor Author

Test report: deploy order on a real platform

Result: pass. With this PR, the service's deployment is created only after its database migration has finished.

What was tested

  • This PR at 868ff7a, installed from the pkg.pr.new packages pinned to that commit (@prisma/composer, @prisma/composer-cli, @prisma/composer-prisma-cloud).
  • alchemy 2.0.0-beta.78, @prisma/orm-postgres 8.0.0-rc.13.

Setup

  • One throwaway project on Prisma's preview platform, created by the deploy and destroyed afterwards.
  • A module with three nodes:
    • a postgres() database with a real Prisma 8 contract and two migration packages (577 and 6 operations);
    • a small compute() service that depends on the database and the bucket;
    • a bucket().
  • No real secrets.

Result: a fresh deploy

All times are UTC on 2026-10-02. The 0.25.0 run used the same kind of module, earlier the same day.

Event 0.25.0 This PR
Migration finished 09:07:33 12:42:43.9
Service deployment started (Composer log) — 12:42:48.4
Service deployment created (platform record) — 12:42:51.1
Service deployment live 09:03:58 12:43:01.3
  • On 0.25.0, the service went live 3 minutes 35 seconds before the migration finished.
  • With this PR, the deployment started 4.5 seconds after the migration finished. The database URL environment row was also held back until the migration finished. The rows that do not depend on the database were written right away.
  • After the deploy, prisma db verify reported "Database marker and schema match contract", migration status reported both migrations applied, and the service answered on its endpoint.

During setup, two earlier attempts failed in the migration step, for a local install reason (see the setup note). In both, the service's deployment and the database URL row were not created. A failed migration blocked the new code, as the PR intends.

Result: a redeploy with no changes

  • The migration was a no-op, and nothing was replaced.
  • The service kept the same single deployment; no new deployment was created.
  • The environment rows and the deployment showed as updates, which matches the steady state on 0.25.0.

Setup note: not caused by this PR

Installing the three preview packages next to @prisma/orm-toolchain 8.0.0-rc.13 gives a peer conflict:

  • @prisma/composer-cli wants @prisma/cli-engine 0.6.2;
  • the toolchain wants 0.6.1.

With the plain install, npm nested a second copy of arktype, and the migration step failed contract validation with "…storage.ref must be an object (was missing)". A clean npm install --legacy-peer-deps fixed it. The migration runner's code is identical to 0.25.0, so this is a packaging issue between those versions, not part of this change.

Not covered

  • A deploy that changes the migration target (a new contract or ref).
  • Windows.
  • Postgres integration tests.

Cleanup

  • prisma-composer destroy --production removed all 16 resources and the project.
  • A GET for the project, service, deployment, database, and bucket returns 404.
  • The service endpoint returns 404.
  • The access token used for the test is revoked.

🤖 Generated with Claude Code

@kristof-siket
kristof-siket merged commit 4b989f3 into main Oct 2, 2026
34 of 35 checks passed
@kristof-siket
kristof-siket deleted the fix/deploy-after-migration branch October 2, 2026 12:50
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