Files
HealthLog/src/lib/db.ts
T
Marc-André Bombeck 9445d89b76 refactor(api,tz): one typed client for every call and one home for wall-clock math
A typed apiFetch wrapper owns the envelope unwrap, the ok-check and the
error read for every client call — two hundred call sites across the
app migrate onto it and a lint rule keeps raw api fetches out for good.
The legacy timezone helpers fold into the tz module and test fixtures
drop personal names.
2026-06-11 02:19:27 +02:00

58 lines
2.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Prisma, PrismaClient } from "@/generated/prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
/**
* v1.4.40 W-POOL — Prisma `pg.Pool` ceiling raised from the library
* default of 10 → 20.
*
* The v1.4.39 empirical cold-mount trace
* (`.planning/round-v1439-empirical-trace.md` § B2) showed thick
* `/api/analytics` holding ≥ 8 of the 10 default pool slots for
* 6.5 s on the maintainer's 347k-row tenant, starving every other Wave-B and
* Wave-C useQuery fan-out for the duration. Coolify's Postgres
* container runs the stock `max_connections` 100 ceiling, so a
* 20-slot Node-side pool sits well under the server's hard cap with
* plenty of headroom for concurrent power-users (≤ 5 simultaneous
* tenants × 20 = 100). Complements the W-POOL `p-limit(4)` cap on
* the analytics fan-out itself — the bounded concurrency keeps any
* single analytics call to ≤ 4 slots, so the remaining 16 stay
* available for every other dashboard query.
*
* Env-overridable via `DATABASE_POOL_MAX` so an operator running
* with a smaller Postgres `max_connections` can dial down without a
* code change.
*/
export function getPoolMax(): number {
const raw = process.env.DATABASE_POOL_MAX;
if (raw) {
const parsed = Number.parseInt(raw, 10);
if (Number.isFinite(parsed) && parsed > 0) return parsed;
}
return 20;
}
function createPrismaClient() {
const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL!,
max: getPoolMax(),
});
return new PrismaClient({ adapter });
}
export const prisma = globalForPrisma.prisma ?? createPrismaClient();
if (process.env.NODE_ENV !== "production") {
globalForPrisma.prisma = prisma;
}
// Prisma's `InputJsonValue` requires an explicit index signature that
// typed application shapes (Zod-validated, hand-written interfaces)
// don't carry. Every JSON-column write would otherwise repeat the same
// `value as unknown as Prisma.InputJsonValue` escape hatch — this one
// helper centralises the cast so the WHY stays in a single place.
export const toJson = <T>(v: T) => v as unknown as Prisma.InputJsonValue;