Skip to main content

Transaction Callsite Audit

Generated: 2026-05-03 Purpose: Identify every interactive db.transaction(...) callsite that must be migrated to getTxDb + ctx.waitUntil(end()) rather than the HTTP driver.

Confirmed interactive transactions (need getTxDb)

FileLinePurpose
server/src/routes/appointments.ts1417Appointment update with conflict check
server/src/routes/auth.ts1792Signup with phone-uniqueness lock
server/src/routes/installments.ts88Create installment plan
server/src/routes/installments.ts431Pay installment
server/src/routes/installments.ts567Bulk installment update
server/src/routes/invoices.tsx375Convert quotation to invoice
server/src/routes/payroll.ts289Payroll run creation
server/src/routes/public-documents.ts421Public document signing
server/src/routes/treatment-plans.ts607Treatment plan creation
server/src/routes/treatment-plans.ts774Treatment plan update
server/src/lib/importer.ts657Bulk-import batch insert + ID-mapping write
server/src/scheduled/installment-invoices.ts73Cron: generate invoice for installment term
server/src/scheduled/appointment-invoices.ts120Cron: generate invoice 48h before appointment
Total confirmed: 13 interactive transaction callsites.

Non-issues (no migration needed)

  • routes/admin.ts:1874DO $$ BEGIN ... END $$ is a PL/pgSQL block, not an interactive tx. Stays on getReadDb.
  • routes/inventory.ts — “transaction” in identifiers refers to stock transactions (the entity), not DB transactions. Stays on getReadDb.
  • lib/schema-ensure.ts:83, :127, :170, :171, :175, :261, :273 — all DO $$ BEGIN ... END $$ PL/pgSQL blocks executed via db.execute(sql\…`)for idempotent schema setup. Not interactive transactions; stays ongetReadDb` (or whatever single-statement driver schema-ensure currently uses).

Migration plan

Each row above gets a dedicated commit. Wrap the existing db.transaction(...) body verbatim inside getTxDb() and ensure ctx.waitUntil(end()) is registered.

Plan-task coverage

The Phase 1 plan (2026-05-03-app-performance-neon-tanstack.md) currently covers the route callsites in Tasks 8–11:
  • Task 8: appointments.ts:1417
  • Task 9: auth.ts:1792
  • Task 10: installments.ts (lines 88, 431, 567)
  • Task 11: payroll.ts:289, treatment-plans.ts:607+:774, public-documents.ts:421

Newly-discovered callsites (additions to plan)

The grep surfaced three callsites the original plan table omitted. They will need their own migration commits — flagging here for Sarmad’s review on which task to fold them into:
  1. routes/invoices.tsx:375 — quotation→invoice conversion. Naturally fits a new “Task 11.5: migrate invoices.tsx tx” or could be added to Task 5 (billing batch), but Task 5 is non-tx-only so a separate tx commit is cleaner.
  2. lib/importer.ts:657 — bulk-import batch insert. Importer runs inside a route handler, so it has a request ctx available; same pattern applies. Needs its own commit (no current task covers it).
  3. scheduled/installment-invoices.ts:73 and scheduled/appointment-invoices.ts:120 — Cron-triggered handlers. These run via scheduled() not fetch(), so the ctx.waitUntil(end()) pattern is identical (the Cloudflare ScheduledEvent also exposes waitUntil). Needs its own commit.
Recommended: add a new Task 11.5 to the plan covering invoices.tsx + importer.ts + the two scheduled files, OR fold them into Task 11 (which already covers a multi-file tx batch).
Needs Sarmad’s review: confirm the four newly-discovered callsites (invoices.tsx, importer.ts, two scheduled handlers) should be migrated in Phase 1 and decide which task to fold them into.