Repository navigation
refactor(db): move Postgres access from drizzle to effect-orm - #330
Conversation
Every table as a PG.table in @hazel/db/tables, generated from drizzle's runtime table config and checked against it: the DDL they render, applied to an empty Postgres, pg_dumps identically to a drizzle-kit push, except that drizzle's UNIQUE constraints are unique indexes of the same name (effect-orm does not model unique constraints). Timestamps read as Date and jsonb documents stay unvalidated, as drizzle had them; ids are branded through the @hazel/schema schemas. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-orm Database.Database is effect-orm over @effect/sql-pg: run/first/query/ execute/transaction, a pooled client for long-running processes and a per-request client for Workers. Transactions pin every statement in their body through fiber context, so the TxFn/TransactionContext threading and makeQuery wrappers are gone. DatabaseError keeps its type classification and gains constraint and sqlState. Every backend-core repository and the proxy auth consumer query through the PG builder over @hazel/db/tables. The Electric where-clause builder renders its own parameterized fragments with unqualified column names. Schema management moves from drizzle-kit push to effect-orm migrations: an initial migration with the schema push left, then an effect-orm baseline. db:migrate adopts a database push built by recording both as applied, and migrates a fresh one from scratch. Callers in apps/* are converted in the next commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cluster crons and workflows, electric-proxy bot auth and access-context cache query through the PG builder; drizzle-orm leaves both packages. The cluster still probes the database at startup with the old retry schedule, since the new pool dials lazily. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Handlers query through db.run over the PG builder instead of transactionAwareExecute and drizzle; unique-violation checks read DatabaseError.constraint and the slug conflict reads its detail. Maintenance scripts and the setup bots command follow, and drizzle-orm and drizzle-kit leave apps/backend. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Chat-sync worker, side-effect, channel-access sync and connect services query through the PG builder. The chat-sync DB harness migrates its container with @hazel/db/migrate instead of db:push, and tests fake or provide the new Database shape. The Bun backend probes the database at startup with the old retry schedule, since the pool dials lazily. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Outbox claim locks the candidate ids (FOR UPDATE SKIP LOCKED), then updates exactly those, in one transaction: an UPDATE ... WHERE id IN (subquery ... LIMIT) may re-evaluate its subquery and over-claim. - Unique-conflict fallbacks (connect conversations, mounts, participants) run their insert in a savepoint, so the recovery lookup does not hit an aborted transaction. - The initial migration creates the seven unique keys drizzle-kit push made constraints as UNIQUE constraints: a fresh database now pg_dumps identically to a pushed one. - Id columns are type-only brands again, as drizzle's $type was, so one stored id outside its schema's checks cannot fail a whole read. - Database.layer probes the database at startup with the old retry schedule for every caller, replacing two copies of the probe; the transaction span is back; the redundant Reactivity layer is gone. - db:migrate and the effect-orm driver require DATABASE_URL instead of falling back to localhost. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
|
Important Review skippedToo many files! This PR contains 161 files, which is 11 over the limit of 150. To get a review, reduce the PR to 150 files or fewer by splitting it into smaller PRs or changing its base branch. Upgrade to a paid plan to raise the limit. This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry. ⚙️ Run configuration
⛔ Files ignored due to path filters (1)
📒 Files selected for processing (161)
You can disable this status message by setting the
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
|
Moves Hazel's Postgres access from drizzle-orm to effect-orm (
@maple-dev/effect-orm0.6.0) over@effect/sql-pg, following Maple's migration (maple#1353). drizzle-orm and drizzle-kit leave every Hazel package.What changes
PG.tableinpackages/db/src/tables, keeping the drizzle names (schema.messagesTable,Message, …). Timestamps still read asDate. jsonb stays unvalidated and ids are type-only brands, as drizzle's$typewas. The 18 enums arePG.enumTypeand the 7 unique keys push made constraints arePG.unique, sogeneratemigrates both.Databaseservice:db.run/db.first/db.query/db.execute/db.transaction.tx/TxFn/TransactionContextthreading,makeQueryandtransactionAwareExecuteare gone, and a nested transaction is a savepoint.DatabaseErrorkeeps itstypeand gainsconstraint,sqlStateanddetail.drizzle-kit pushis replaced bypackages/db/migrations, written byeffect-orm generate.initial_schemais exactly what push produced, enums and unique constraints included; a fresh database pg_dumps identically to a pushed one.bun run db:migrateadopts a pushed database by recordinginitial_schemaas applied without running it, and builds a fresh one from scratch.db:generate,db:check,db:statusanddb:verifycome from effect-orm.db:verifyreports no drift on a fresh database, a pushed one, or a copy of the local dev database.Behavior notes
FOR UPDATE SKIP LOCKED), then updates exactly those, in one transaction. It replaces the old CTE with the same guarantee.error.constraint; the old check never matched.DateTimetoDatebefore writing; the oldas anyhid this.SELECT 1COUNT(DISTINCT …) FILTERin the single-DM lookupreset-all/merge-org-duplicatesmaintenance queriesTRUNCATETesting
0.6.0 bump (latest commit): typecheck is clean, and the 263 tests that don't need Docker pass. The Docker-backed suites (db service, outbox, side effects, chat-sync integration) have not been re-run on this commit because local Docker stopped responding. The schema itself was verified against real Postgres: fresh, pushed and local-copy databases all
verifywith no drift.Typecheck: clean in
packages/{db,backend-core,auth,setup,domain,infra},apps/{backend (both tsconfigs),cluster,electric-proxy,bot-gateway,web}and the alchemy config.Tests:
vitestpassed 286 of 286 across 41 files before the 0.6.0 bump, including the chat-sync integration tests and the@hazel/dbservice tests against real Postgres 17 (Docker). The db tests cover rollback, savepoints, error classification and the recovery-after-conflict pattern.Migrations:
db:migratepg_dumps identically to adrizzle-kit pushdatabase.db:migrateadopted a copy of the local dev database and is idempotent.generatereports no schema changes against the tables.End to end, Bun: run against a copy of the local dev database, with this branch's backend and cluster.
create-bot.ts.@channelmessage ran the cluster notification workflow, which inserted the notification and incrementednotificationCount.End to end, Workers: a scratch Worker in local workerd (
nodejs_compat) usedDatabase.makeRequestConnectionper request. Repositories, pagination,Datedecoding, a transaction's txid and a rollback all worked across consecutive requests.Not tested: the RPC/OAuth paths that need a real Clerk session,
alchemy dev/deploy (real Hyperdrive), and Electric shape sync through the proxy in a browser.Deploy notes
DATABASE_URL=<prd API role URL> bun run db:migrateonce (packages/db). It records the existing pushed schema as applied and changes nothing else.db:migratenow refuses to run withoutDATABASE_URL.DATABASE_URL=<…> bun run db:verifyshould report no drift.Known gaps
--custommigration.packages/setup'sbotscommand never providesDatabase.Database(hidden by a cast), so it fails at runtime. Left as is.🤖 Generated with Claude Code