From f8a88d13619abfbfc236aea14e3dcfddb0269a34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 22 Oct 2026 07:25:00 +0200 Subject: [PATCH] perf(auth): read the operator settings once per request An app boot's /api/auth/me read the AppSettings singleton six times, each reader with its own few columns: the MFA policy, the Assistant switches, the module availability, the provider presence, the AI usage bounds and the reasoning controls. They now take their columns from one read, memoised for the request and read afresh inside a background job like the readers themselves. No answer changes. --- src/lib/ai/capabilities/usage.ts | 10 +---- src/lib/ai/provider.ts | 16 +------ src/lib/ai/reasoning/controls.ts | 6 +-- src/lib/auth/mfa-enrollment.ts | 6 +-- src/lib/feature-flags/index.ts | 13 +----- src/lib/modules/operator-availability.ts | 7 +-- src/lib/settings/operator-row.ts | 56 ++++++++++++++++++++++++ 7 files changed, 68 insertions(+), 46 deletions(-) create mode 100644 src/lib/settings/operator-row.ts diff --git a/src/lib/ai/capabilities/usage.ts b/src/lib/ai/capabilities/usage.ts index e3eda8f27..decf7e4ff 100644 --- a/src/lib/ai/capabilities/usage.ts +++ b/src/lib/ai/capabilities/usage.ts @@ -17,6 +17,7 @@ * (`PUT /api/auth/me/ai-usage`); a managed profile has no choice of its own * and follows the instance default. */ +import { readOperatorRow } from "@/lib/settings/operator-row"; import { prisma } from "@/lib/db"; import { getEvent } from "@/lib/logging/context"; import { memoizePerRequest } from "@/lib/request-cache"; @@ -83,14 +84,7 @@ export function aiUsageSettingsFromRow( export function loadAiUsageSettings(): Promise { const read = async (): Promise => { try { - const row = await prisma.appSettings.findUnique({ - where: { id: "singleton" }, - select: { - aiUsageMaxLevel: true, - aiUsageDefaultLevel: true, - aiSyncRegenGapHours: true, - }, - }); + const row = await readOperatorRow(); return aiUsageSettingsFromRow(row); } catch { getEvent()?.addWarning( diff --git a/src/lib/ai/provider.ts b/src/lib/ai/provider.ts index 9b03bfc5d..9c788bbee 100644 --- a/src/lib/ai/provider.ts +++ b/src/lib/ai/provider.ts @@ -1,4 +1,5 @@ import type { AIProvider, CompletionResult } from "./types"; +import { readOperatorRow } from "@/lib/settings/operator-row"; import { bindResponseTimeout } from "./effective-timeout"; import { bindReasoningEffort } from "./reasoning-effort"; import { prisma } from "@/lib/db"; @@ -1145,16 +1146,6 @@ const PRESENCE_USER_SELECT = { aiResponseTimeoutSeconds: true, } as const; -const PRESENCE_SETTINGS_SELECT = { - adminAiKeyEncrypted: true, - adminAiModel: true, - adminAiBaseUrl: true, - adminCodexConnectionStatus: true, - adminCodexAccessTokenEncrypted: true, - adminCodexRefreshTokenEncrypted: true, - adminCodexAccountIdEncrypted: true, -} as const; - const NO_PRESENCE: ProviderPresence = Object.freeze({ entries: [], localOcrEnabled: false, @@ -1217,10 +1208,7 @@ export function probeProviderChain( where: { id: recordId }, select: PRESENCE_USER_SELECT, }), - prisma.appSettings.findUnique({ - where: { id: "singleton" }, - select: PRESENCE_SETTINGS_SELECT, - }), + readOperatorRow(), ]); if (!row) return NO_PRESENCE; const policy = providerCredentialPolicy(authority, row.managedProfileAt); diff --git a/src/lib/ai/reasoning/controls.ts b/src/lib/ai/reasoning/controls.ts index 7b1420c36..a34655774 100644 --- a/src/lib/ai/reasoning/controls.ts +++ b/src/lib/ai/reasoning/controls.ts @@ -5,6 +5,7 @@ * Server-only. The decision itself is the pure `resolveReasoning` in * `./resolve.ts`; this file only gathers its inputs. */ +import { readOperatorRow } from "@/lib/settings/operator-row"; import { prisma } from "@/lib/db"; import { annotate, getEvent } from "@/lib/logging/context"; import { memoizePerRequest } from "@/lib/request-cache"; @@ -70,10 +71,7 @@ function loadMemoisedControls(): Promise { "ai-reasoning-controls", async () => { try { - const row = await prisma.appSettings.findUnique({ - where: { id: "singleton" }, - select: { aiReasoningEnabled: true, aiReasoningMaxEffort: true }, - }); + const row = await readOperatorRow(); return { enabled: row?.aiReasoningEnabled ?? true, maxEffort: parseReasoningMaxEffort(row?.aiReasoningMaxEffort), diff --git a/src/lib/auth/mfa-enrollment.ts b/src/lib/auth/mfa-enrollment.ts index 4a78ffd7b..89378c827 100644 --- a/src/lib/auth/mfa-enrollment.ts +++ b/src/lib/auth/mfa-enrollment.ts @@ -16,6 +16,7 @@ * out; the operator/CLI escape hatch (`scripts/disable-mfa.ts`, toggling the * policy off) is the recovery path of last resort. */ +import { readOperatorRow } from "@/lib/settings/operator-row"; import { cookies } from "next/headers"; import { prisma } from "@/lib/db"; import { shouldEmitSecureCookie } from "@/lib/auth/secure-cookie"; @@ -44,10 +45,7 @@ export async function resolveMfaEnrollmentRequired( // Already has a confirmed TOTP secret → enrolled, nothing required. if (user.totpConfirmedAt) return false; - const settings = await prisma.appSettings.findUnique({ - where: { id: "singleton" }, - select: { mfaRequired: true }, - }); + const settings = await readOperatorRow(); const policyApplies = Boolean(settings?.mfaRequired) || user.mfaEnforced; if (!policyApplies) return false; diff --git a/src/lib/feature-flags/index.ts b/src/lib/feature-flags/index.ts index 5d1b613bc..e46b5ad0f 100644 --- a/src/lib/feature-flags/index.ts +++ b/src/lib/feature-flags/index.ts @@ -1,4 +1,4 @@ -import { prisma } from "@/lib/db"; +import { readOperatorRow } from "@/lib/settings/operator-row"; import { getEvent } from "@/lib/logging/context"; import { memoizePerRequest } from "@/lib/request-cache"; @@ -57,16 +57,7 @@ export function loadAssistantSwitches(): Promise { "assistant-flags", async () => { try { - const settings = await prisma.appSettings.findUnique({ - where: { id: "singleton" }, - select: { - assistantEnabled: true, - assistantCoachEnabled: true, - assistantBriefingEnabled: true, - assistantInsightStatusEnabled: true, - assistantDocumentAiEnabled: true, - }, - }); + const settings = await readOperatorRow(); return resolveAssistantFlags({ enabled: settings?.assistantEnabled ?? true, coach: settings?.assistantCoachEnabled ?? true, diff --git a/src/lib/modules/operator-availability.ts b/src/lib/modules/operator-availability.ts index d90b44a10..42c045c3e 100644 --- a/src/lib/modules/operator-availability.ts +++ b/src/lib/modules/operator-availability.ts @@ -36,7 +36,7 @@ * 0343 folded the blob's `coach` key into the switch and removed it, and a * `coach` key that still turns up in the blob is ignored. */ -import { prisma } from "@/lib/db"; +import { readOperatorRow } from "@/lib/settings/operator-row"; import { getAssistantFlags } from "@/lib/feature-flags"; import { getEvent } from "@/lib/logging/context"; import { memoizePerRequest } from "@/lib/request-cache"; @@ -84,10 +84,7 @@ export function resolveOperatorAvailability( /** The raw availability blob; a read error reads as "nothing disabled". */ async function loadAvailabilityBlob(): Promise { try { - const settings = await prisma.appSettings.findUnique({ - where: { id: "singleton" }, - select: { moduleAvailabilityJson: true }, - }); + const settings = await readOperatorRow(); return settings?.moduleAvailabilityJson ?? null; } catch { getEvent()?.addWarning( diff --git a/src/lib/settings/operator-row.ts b/src/lib/settings/operator-row.ts new file mode 100644 index 000000000..00fd0972d --- /dev/null +++ b/src/lib/settings/operator-row.ts @@ -0,0 +1,56 @@ +/** + * The operator's settings that every app boot reads, in one statement per + * request. + * + * `/api/auth/me` resolved six answers out of the `AppSettings` singleton (the + * MFA policy, the Assistant switches, the module availability, the provider + * presence, the AI usage bounds and the reasoning controls), each with its own + * `SELECT` of a few columns, so one client boot read the same row six times. + * Each reader now takes its columns from this one read, memoised for the + * request (and read afresh for every call inside a background job, whose one + * event spans a whole multi-user pass, like the readers it serves). + * + * A reader keeps its own error handling: a failed read rejects here, and the + * memo forgets a rejected read so the next caller tries again. + */ +import type { Prisma } from "@/generated/prisma/client"; +import { prisma } from "@/lib/db"; +import { memoizePerRequest } from "@/lib/request-cache"; + +export const OPERATOR_ROW_SELECT = { + mfaRequired: true, + assistantEnabled: true, + assistantCoachEnabled: true, + assistantBriefingEnabled: true, + assistantInsightStatusEnabled: true, + assistantDocumentAiEnabled: true, + moduleAvailabilityJson: true, + adminAiKeyEncrypted: true, + adminAiModel: true, + adminAiBaseUrl: true, + adminCodexConnectionStatus: true, + adminCodexAccessTokenEncrypted: true, + adminCodexRefreshTokenEncrypted: true, + adminCodexAccountIdEncrypted: true, + aiUsageMaxLevel: true, + aiUsageDefaultLevel: true, + aiSyncRegenGapHours: true, + aiReasoningEnabled: true, + aiReasoningMaxEffort: true, +} as const satisfies Prisma.AppSettingsSelect; + +export type OperatorRow = Prisma.AppSettingsGetPayload<{ + select: typeof OPERATOR_ROW_SELECT; +}>; + +export function readOperatorRow(): Promise { + return memoizePerRequest( + "operator-settings-row", + () => + prisma.appSettings.findUnique({ + where: { id: "singleton" }, + select: OPERATOR_ROW_SELECT, + }), + { freshInBackground: true }, + ); +}