diff --git a/.server-changes/webapp-sentry-fingerprint-p1001.md b/.server-changes/webapp-sentry-fingerprint-p1001.md new file mode 100644 index 0000000000..dd2f1ecc55 --- /dev/null +++ b/.server-changes/webapp-sentry-fingerprint-p1001.md @@ -0,0 +1,6 @@ +--- +area: webapp +type: improvement +--- + +Group Prisma P1001 ("Can't reach database server") errors into a single Sentry issue via a `beforeSend` fingerprint rule, so DB outages no longer fan out into hundreds of distinct issues that bury other alerts. Adds a small extensible rule table for future collapsing rules. diff --git a/apps/webapp/sentry.server.ts b/apps/webapp/sentry.server.ts index ee84c1d0e3..7f8f6deb62 100644 --- a/apps/webapp/sentry.server.ts +++ b/apps/webapp/sentry.server.ts @@ -1,6 +1,25 @@ import * as Sentry from "@sentry/remix"; import { addOtelTraceContextToEvent } from "./app/utils/sentryTraceContext.server"; +// Rules for collapsing high-volume errors into a single Sentry issue. +// Without this, e.g. a DB outage produces hundreds of distinct issues — +// one per stack trace — which buries other alerts. Add a new rule here +// when you spot another error that fans out across call sites. Keep +// predicates cheap (string compare, not regex over stack traces). +const FINGERPRINT_RULES: Array<{ + match: (err: { code?: unknown; errorCode?: unknown; name?: unknown }) => boolean; + fingerprint: string; + tags?: Record; +}> = [ + { + // Prisma surfaces P1001 on `code` for KnownRequestError (mid-query connection drop) + // and `errorCode` for InitializationError (client failed to connect at startup). + match: (err) => err.code === "P1001" || err.errorCode === "P1001", + fingerprint: "prisma-p1001-db-unreachable", + tags: { db_unreachable: "true" }, + }, +]; + if (process.env.SENTRY_DSN) { console.log("🔭 Initializing Sentry"); @@ -29,6 +48,20 @@ if (process.env.SENTRY_DSN) { // and stay visible. ignoreErrors: ["queryRoute() call aborted", /^ServiceValidationError(?::|$)/], includeLocalVariables: false, + + beforeSend(event, hint) { + const err = hint.originalException as + | { code?: unknown; errorCode?: unknown; name?: unknown } + | undefined; + if (!err) return event; + + const rule = FINGERPRINT_RULES.find((r) => r.match(err)); + if (!rule) return event; + + event.fingerprint = [rule.fingerprint]; + if (rule.tags) event.tags = { ...event.tags, ...rule.tags }; + return event; + }, }); Sentry.addEventProcessor(addOtelTraceContextToEvent);