refactor(db): extract the P2002 predicate into one shared helper

The Prisma unique-constraint (P2002) narrowing predicate had been
hand-copied into five call sites. Fold them into a single isP2002 in
src/lib/prisma-errors.ts and import it everywhere (the two step-drain
sites keep their local isUniqueConstraintViolation name via an import
alias). Behaviour-identical; one place to chase a future P2002-semantics
change instead of five.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marc-André BombeckandClaude Opus 4.8 committed 2026-06-04 13:26:07 +02:00
1 parent 9c39afa352
commit ba4349eda0
6 files changed
+29 -51

No files matched your search

@@ -38,6 +38,7 @@
* `rollup-full-backfill` boot-time converging-backfill pattern.
*/
import type { PrismaClient } from "@/generated/prisma/client";
import { isP2002 as isUniqueConstraintViolation } from "@/lib/prisma-errors";
import { dailyStatsExternalId } from "./apple-health-mapping";
import {
@@ -100,15 +101,6 @@ export interface StepConsolidationSummary {
};
}
/** True for a Prisma unique-constraint violation (P2002). */
function isUniqueConstraintViolation(err: unknown): boolean {
return (
typeof err === "object" &&
err !== null &&
"code" in err &&
(err as { code?: unknown }).code === "P2002"
);
}
export interface StepConsolidationOptions {
/** Limit the pass to a single user. Default = every user. */
@@ -41,6 +41,7 @@
* production standalone image strips `tsx`, so this can never be a CLI.
*/
import type { MeasurementType, PrismaClient } from "@/generated/prisma/client";
import { isP2002 as isUniqueConstraintViolation } from "@/lib/prisma-errors";
import {
dailyStatsExternalId,
@@ -77,19 +78,6 @@ export const DENSE_INTRADAY_RETENTION_DAYS = 14;
/** The daily-stats externalId prefix marks an already-collapsed row. */
const DAILY_STATS_PREFIX = "stats:";
/**
* True for a Prisma unique-constraint violation (P2002). Mirrors the
* `consolidate-legacy-steps.ts` precedent verbatim so the two drains
* classify the same error the same way.
*/
function isUniqueConstraintViolation(err: unknown): boolean {
return (
typeof err === "object" &&
err !== null &&
"code" in err &&
(err as { code?: unknown }).code === "P2002"
);
}
export interface DenseIntradayRetentionSummary {
dryRun: boolean;
+1 -10
View File
@@ -41,6 +41,7 @@
* unregistered queue silently never drains.
*/
import { prisma } from "@/lib/db";
import { isP2002 } from "@/lib/prisma-errors";
import { annotate } from "@/lib/logging/context";
import { getGlobalBoss } from "@/lib/jobs/boss-instance";
import { DEFAULT_TIMEZONE } from "@/lib/tz/format";
@@ -53,16 +54,6 @@ import { recomputeMedicationComplianceForEvent } from "@/lib/rollups/medication-
export const INTAKE_SLOT_DEDUP_QUEUE = "intake-slot-dedup";
/** Narrow a thrown Prisma error to the P2002 unique-constraint code. */
function isP2002(err: unknown): boolean {
return (
typeof err === "object" &&
err !== null &&
"code" in err &&
(err as { code: unknown }).code === "P2002"
);
}
/**
* Serial concurrency — the populator walks a user's intake events and
* writes per slot; concurrency-1 keeps it from crowding the request
@@ -17,21 +17,13 @@
import type { PrismaClient } from "@/generated/prisma/client";
import { prisma as defaultPrisma } from "@/lib/db";
import { isP2002 } from "@/lib/prisma-errors";
import { resolveCanonicalSlotInstant } from "@/lib/medications/scheduling/resolve-slot-instant";
import type { WorkerScheduleRow } from "@/lib/medications/scheduling/worker-helpers";
import type { InjectionSiteKey } from "@/lib/medications/injection-sites";
type PrismaLike = Pick<PrismaClient, "medication" | "medicationIntakeEvent">;
/** Narrow a thrown Prisma error to the P2002 unique-constraint code. */
function isP2002(err: unknown): boolean {
return (
typeof err === "object" &&
err !== null &&
"code" in err &&
(err as { code: unknown }).code === "P2002"
);
}
/** The minimal intake-row shape the slot upsert reads + returns. */
export interface SlotIntakeRow {
+24
View File
@@ -0,0 +1,24 @@
/**
* Shared Prisma error narrowing helpers.
*
* The P2002 (unique-constraint violation) predicate was hand-copied into
* five call sites (rollup persist, intake-slot dedup, slot upsert, legacy
* step consolidation, dense-intraday retention). v1.11.2 folds them into
* this single helper so a future change to the P2002 semantics has one
* place to live instead of five.
*
* Deliberately structural (`typeof === "object"` + `"code" in err` +
* `code === "P2002"`) rather than `instanceof Prisma.PrismaClientKnownRequestError`
* so it survives client-bundling quirks while staying specific to the
* unique-constraint code.
*/
/** Narrow a thrown Prisma error to the P2002 unique-constraint code. */
export function isP2002(err: unknown): boolean {
return (
typeof err === "object" &&
err !== null &&
"code" in err &&
(err as { code: unknown }).code === "P2002"
);
}
+1 -10
View File
@@ -30,6 +30,7 @@
import { prisma } from "@/lib/db";
import { getGlobalBoss } from "@/lib/jobs/boss-instance";
import { annotate } from "@/lib/logging/context";
import { isP2002 } from "@/lib/prisma-errors";
import {
collapseRollupRowsBySource,
loadUserSourcePriority,
@@ -437,16 +438,6 @@ async function runRollupAggregate(input: {
* with `ON CONFLICT DO UPDATE` so the round-trip count is bounded
* regardless of fan-out width.
*/
/** Narrow a thrown Prisma error to the P2002 unique-constraint code. */
function isP2002(err: unknown): boolean {
return (
typeof err === "object" &&
err !== null &&
"code" in err &&
(err as { code: unknown }).code === "P2002"
);
}
async function persistRollupRows(
userId: string,
granularity: RollupGranularity,