mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
A restore on a freshly booted instance failed its clearing step with a deadlock (40P01) while the boot-time step consolidation was folding the same account. A consolidation day updates or mints the day's total and then soft-deletes the day's per-sample rows; the restore deletes every reading of the account in one statement. When the restore reached a day's samples before its total, each side held what the other needed next. The restore already took an advisory lock per account before its first delete. Every day transaction of the passes built on runConsolidation (step and mean consolidation, the per-sample cumulative drain, the dense intraday retention) now takes the same lock in shared mode as its first statement. A restore that starts mid-day waits for that day to commit; a day that would start while a restore runs is refused at once, and the pass leaves that account for its next run instead of folding rows the restore is about to replace. The passes still share the lock with each other. A new integration test builds the lock order that deadlocked, with a row trigger holding the consolidation on the day's total while the restore starts; before this change the consolidation was cancelled with a deadlock. A guard test requires every transaction in a pass on runConsolidation to open with the shared lock. Refs #1031
src/
Application source. HealthLog is a Next.js 16 App Router project (output: "standalone", React 19, TypeScript strict). The path alias @/* resolves to this directory. Everything a request touches lives under here; the layout below follows the request flow rather than alphabetical order.
Request flow
client → src/proxy.ts → src/app/<route>/route.ts → src/lib/api-handler.ts → handler body → src/lib/api-response.ts → client
proxy.ts— the HTTP edge (Next.js 16's renamedmiddleware). Public-path allowlist, demo-mode mutation block, server-side onboarding redirect, worker-only refusal,x-request-id+ CSP nonce generation, every security header.app/— App Router pages (RSC by default) and API routes. Every API route wraps its handler inapiHandler. Seeapp/README.md.lib/— the server/runtime toolkit:apiHandler, auth, crypto, rollups, AI, notifications, pg-boss jobs, integrations, rate limiting. Seelib/README.md.components/— React components (shadcn/ui new-york, kebab-case files). Seecomponents/README.md.
Other directories
hooks/— shared React hooks (use-prefix, kebab-case).types/— shared TypeScript declarations.generated/prisma/— the generated Prisma client (do not edit; regenerated byprisma generate). Imported as@/generated/prisma.instrumentation.ts— Next.js startup hook; boots the pg-boss worker registry and boot-time backfill queues.__tests__/— cross-cutting unit tests; most tests live in__tests__/folders next to the code they cover.
Conventions
See ../CLAUDE.md for the full set. The load-bearing ones: RSC by default ("use client" only when a hook/state/browser API needs it); every API route wraps apiHandler and parses its body with Zod safeParse; userId is always narrowed from session or Bearer, never accepted as a body field; TanStack Query keys come only from src/lib/query-keys/; outbound HTTP goes through src/lib/safe-fetch.ts.