From e00943ff7f27a517acdd8c556d2e2cf8580062cf 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(day): read the record sections six at a time The day view started about twenty-five section reads at once beside the readings, scores and movement, more than the pool holds, so a neighbouring request waited for a connection. The sections now run six at a time and come back in the same order. --- src/lib/day/load-day.ts | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/lib/day/load-day.ts b/src/lib/day/load-day.ts index ae97357ee..0d517c770 100644 --- a/src/lib/day/load-day.ts +++ b/src/lib/day/load-day.ts @@ -14,6 +14,8 @@ * once in `sections`. Nothing here writes, and nothing here talks to a model; * `model-projection.ts` is the one way a day leaves for one. */ +import pLimit from "p-limit"; + import type { MeasurementType } from "@/generated/prisma/enums"; import { readDayMovement } from "@/lib/dawarich/movement"; @@ -35,6 +37,9 @@ import { loadUserSourcePriority } from "@/lib/rollups/measurement-read"; import { localDayWindow } from "@/lib/tz/local-day"; import { resolveUserTimezone } from "@/lib/tz/resolver"; +/** Record sections read at once for one day (see `loadDay`). */ +const DAY_SECTION_CONCURRENCY = 6; + const NO_READINGS: DayReadings = { rowsByType: new Map(), night: null, @@ -99,6 +104,7 @@ export async function loadDay(args: LoadDayArgs): Promise { ? await loadUserSourcePriority(recordId) : null; + const sectionLimit = pLimit(DAY_SECTION_CONCURRENCY); const recordSections = [...access.readable].filter( (section) => RECORD_SECTION_READERS[section] !== undefined, ); @@ -115,13 +121,20 @@ export async function loadDay(args: LoadDayArgs): Promise { priority, ) : Promise.resolve(NO_READINGS), + // About twenty-five section readers, each a query or two, beside the + // readings, scores and movement: unbounded they took more connections + // than the pool holds, and a neighbouring request waited for a slot. + // Six at a time keeps the day under a third of the pool; the parts come + // back in section order as before. Promise.all( recordSections.map((section) => - ( - RECORD_SECTION_READERS[section] as ( - frame: DayFrame, - ) => Promise - )(frame), + sectionLimit(() => + ( + RECORD_SECTION_READERS[section] as ( + frame: DayFrame, + ) => Promise + )(frame), + ), ), ), scoresReadable