Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
eff3a65
chore(db): drizzle-orm and drizzle-kit 1.0.0-rc.4 on the existing dri…
Makisuo Sep 14, 2026
3d28e10
docs(db): record the drizzle effect-driver spike and re-gate Phase B
Makisuo Sep 14, 2026
d986e63
docs(db): Phase B needs drizzle rc.5, which already exists as an npm …
Makisuo Sep 14, 2026
9f22bb0
feat(db): Effect-native drizzle over @effect/sql-pg, platform layer
Makisuo Sep 14, 2026
ca3c769
feat(db): move every service onto the Effect-native database contract
Makisuo Sep 14, 2026
5e1bf9c
Merge origin/main into chore/drizzle-v1-rc4
Makisuo Sep 16, 2026
e3dc6cc
Merge origin/main into chore/drizzle-v1-rc4
Makisuo Sep 16, 2026
f1bf5bf
feat(db): preflight the migrations-table upgrade before a v1 migrate
Makisuo Sep 16, 2026
c747578
fix(db): preflight only generates an UPDATE for a renumbered row with…
Makisuo Sep 16, 2026
90717b9
fix(db): run the PlanetScale preflight through the package script
Makisuo Sep 16, 2026
875ad5b
fix(db): preflight resolves a historical blob only when it maps to on…
Makisuo Sep 16, 2026
cd3a7ca
fix(backend): classify code-less pg connection failures and late scop…
Makisuo Sep 17, 2026
4ffa271
fix(db): close the gaps between node-postgres and the old postgres.js…
Makisuo Sep 17, 2026
49119d6
Merge origin/main into chore/drizzle-v1-rc4
Makisuo Sep 17, 2026
fc06b07
Merge origin/main into chore/drizzle-v1-rc4
Makisuo Sep 18, 2026
65557f7
Merge origin/main into chore/drizzle-v1-rc4
Makisuo Sep 18, 2026
e05243d
feat(db): preflight lists the migrations the v1 migrator will apply
Makisuo Sep 18, 2026
9e66e17
fix(db): final review fixes for the v1 migration path and the pool scope
Makisuo Sep 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,21 +156,31 @@ Relational state (issues, alert rules, dashboards, org config, keys) is Drizzle/
`packages/db/src/schema/`, on the PlanetScale `main` branch (prd — the only stage with a
database), reached from Workers via the Hyperdrive binding `MAPLE_DB`.

- Drizzle is Effect-native (`drizzle-orm/effect-postgres` over `@effect/sql-pg`, node-postgres
underneath): `Database.execute` takes an Effect callback, queries are `yield*`ed, and
`db.transaction` takes an Effect callback. Driver failures become `DatabaseError` at that
boundary; a `Schema.TaggedError` failed inside a transaction rolls it back and reaches the caller
as itself. Raw `db.execute(sql…)` returns the driver's result object — wrap it in `rawRows`.
- App code keeps epoch-ms numbers and converts at the drizzle boundary — use `msToDate` /
`dateToMs` from `packages/backend/src/platform/time.ts` rather than bare `new Date(ms)` /
`.getTime()`, including inside Promise-land helpers. Never read driver write-result shapes
— use `.returning()` + length. `count(*)` needs `::int` (bigint → string).
`.getTime()`. Never read driver write-result shapes — use `.returning()` + length. `count(*)`
needs `::int` (bigint → string).
- Layers: `DatabasePgLive` (Workers) and `DatabasePgliteLive` (tests/local; `createTestDb()` in
`packages/backend/src/platform/test-pglite.ts`).
- One Postgres connection per invocation — request, cron tick, or Workflow run — created lazily and
- One Postgres pool per invocation — request, cron tick, or Workflow run — created lazily and
closed at the boundary, which is Cloudflare's documented Hyperdrive shape. The single primitive is
`makePgConnectionScope` in `packages/backend/src/platform/pg-connection-scope.ts`; `pgConnectionMiddleware`
installs it for HTTP, `withPgConnectionScope` for cron. Sockets are request-bound on Workers, so a
connection may be reused freely WITHIN an invocation but must never outlive it. `max` is 5
(a ceiling, not a reservation — capping it at 1 serialized cron ticks and cost 3–6x on p50) and the
dial is bounded so a stall lands as `error.type = CONNECT_TIMEOUT` instead of hanging.
- Migrations: `bun run --cwd packages/db db:generate`; CI applies them against the branch's DIRECT
port 5432 (never a pooler) before `alchemy deploy`. PGlite applies them at layer build.
`makePgConnectionScope` in `packages/backend/src/platform/pg-connection-scope.ts`;
`withPgConnectionScope` installs it around each worker's request handler and cron tick. Sockets are
request-bound on Workers, so a connection may be reused freely WITHIN an invocation but must never
outlive it. `max` is 5 (a ceiling, not a reservation — capping it at 1 serialized cron ticks and cost
3–6x on p50). The 10s bound is on each client's DIAL, never the pool: pg-pool applies a pool-level
`connectionTimeoutMillis` to queue waits too. A stalled dial lands as `error.type = ConnectionError`
(a refused one carries the socket code, `ECONNREFUSED`). Fork DB work off a request only with
`forkRequestScoped`, which interrupts it at the response but lets a DB call already under way finish.
- Migrations: `bun run --cwd packages/db db:generate`. Production is applied BY HAND before the
Worker deploy: `bun run --cwd packages/db ps:migrations-preflight main` (read-only; the v1
migrator refuses unmatched rows and replays unrecorded folders), then `bun run migrate:prod`
against the DIRECT port 5432 (never a pooler). PGlite applies them at layer build.
- **PR preview deploys are label-gated** (2026-08, cost — re-enabled by `fd00bcd412`). A PR gets a
preview only while it carries the `preview` label; `deploy-pr-preview.yml` triggers on
`opened, reopened, synchronize, labeled, unlabeled, closed` and tears the stack down the moment
Expand Down
4 changes: 2 additions & 2 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@
"@maple/widgets": "workspace:*",
"@tinybirdco/sdk": "catalog:tinybird",
"alchemy": "catalog:alchemy",
"drizzle-orm": "^0.45.1",
"drizzle-orm": "1.0.0-rc.5-5935859",
"effect": "catalog:effect"
},
"devDependencies": {
"@cloudflare/workers-types": "catalog:alchemy",
"@effect/language-service": "catalog:effect",
"@electric-sql/pglite": "^0.5.2",
"@electric-sql/pglite": "^0.5.6",
"@types/node": "catalog:tooling",
"atmn": "^1.1.17",
"typescript": "catalog:tooling",
Expand Down
Loading
Loading