perf(insights,api): always answer instantly and regenerate in the background

The insights read path served a cold cache by generating inline — up to
ten seconds on first visit — and the nightly warm pass structurally
excluded evening visitors: their visit stamped the cache, the half-four
run skipped it as fresh, and it expired exactly at the next evening's
visit. Stale content now serves immediately with its timestamp while
one background job revalidates, the warm threshold drops to an hour,
forced warming fills both message locales, the analytics caches serve
stale-while-revalidate, the profile endpoint parallelises its reads,
the bug-report status drops a write per call, the umami proxy degrades
to a no-op script instead of an error, the insights route sheds its
heaviest static imports, and the medication page prefetches on intent.
This commit is contained in:
Marc-André Bombeck committed 2026-06-11 19:05:49 +02:00
1 parent ceeb818f41
commit 78fe82e348
19 files changed
+319 -101

No files matched your search

+7 -8
View File
@@ -8,9 +8,8 @@
* server-side (log annotation only) so we don't hand regular users a probe.
*/
import { apiHandler, requireAuth } from "@/lib/api-handler";
import { apiSuccess, apiError } from "@/lib/api-response";
import { apiSuccess } from "@/lib/api-response";
import { annotate } from "@/lib/logging/context";
import { checkRateLimit } from "@/lib/rate-limit";
import { prisma } from "@/lib/db";
import { cached, caches, type ServerCache } from "@/lib/cache/server-cache";
@@ -47,12 +46,12 @@ async function buildBugreportStatusValue(): Promise<BugreportStatusCacheValue> {
export const GET = apiHandler(async () => {
const { user } = await requireAuth();
// Light rate limit — the endpoint is cheap, but no reason to let a logged-in
// client hammer Postgres in a loop.
const rl = await checkRateLimit(`bugreport-status:${user.id}`, 30, 60 * 1000);
if (!rl.allowed) {
return apiError("Rate limit exceeded", 429);
}
// No per-request rate limit: the payload rides the singleton server
// cache below, so a cache hit costs zero Postgres round-trips — while
// the former rate-limit bucket was an unconditional Postgres UPSERT
// per request, i.e. the limiter WAS the per-request DB load it claimed
// to prevent. The route stays authenticated; the only uncached work is
// one `appSettings` read per 60 s process-wide.
// Cache the global app-settings shape on a singleton key (per blueprint §3).
// `isAdmin` lives outside the cache because it varies per request. The