From c8f9ae8047f94f2cfdc6294284a5ec1fdacdbf3e Mon Sep 17 00:00:00 2001 From: Andrei Laas Date: Thu, 24 Sep 2026 11:52:36 +0200 Subject: [PATCH] =?UTF-8?q?Vor=C3=BCbergehend=20messen,=20woher=20die=20an?= =?UTF-8?q?derthalb=20Sekunden=20der=20Akte=20kommen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Die Akte braucht beim Oeffnen rund 1,5 s. Die Datenbank ist es dem Anschein nach nicht: eine Rundreise kostet 0,5 ms (nicht 36, wie der Kommentar hier behauptete), und om_reporting_lines misst 60 ms. Damit laegen unter zehn Prozent der Zeit in den Abfragen. Statt weiter zu raten -- heute schon zweimal danebengelegen -- schreibt loadEmployeeDetail eine Zeile ins Protokoll des Containers: Zeit fuer die Person, fuer die Hauptabfrage, fuer die Nachschlaege, insgesamt, und wie viele Berichtslinien uebertragen wurden. Wieder entfernen, sobald die Ursache feststeht. --- lib/employee-detail-data.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lib/employee-detail-data.ts b/lib/employee-detail-data.ts index 1321552..5b0e235 100644 --- a/lib/employee-detail-data.ts +++ b/lib/employee-detail-data.ts @@ -19,7 +19,21 @@ import { offeneStellenAbfrage, resolveOpenPositions, type OffeneStelle } from ". // darüber, was später passiert ist. Ausgeschrieben ist sichtbar, welche Spalte // durch zeitstempel() geht — bei selectAll() wäre es unsichtbar falsch. +// ── Vorübergehende Messung ──────────────────────────────────────── +// +// Die Akte braucht beim Öffnen rund anderthalb Sekunden, und es ist unklar, +// wovon. Die Datenbank ist es dem Anschein nach nicht: eine Rundreise kostet +// 0,5 ms, om_reporting_lines 60 ms. Statt weiter zu raten, misst diese Zeile +// die drei Abschnitte und schreibt eine Zeile ins Protokoll des Containers. +// +// **Wieder entfernen**, sobald die Ursache feststeht. +function messen(id: string, marken: [string, number][]) { + const teile = marken.map(([name, ms]) => `${name} ${Math.round(ms)}ms`).join(", "); + console.info(`[Akte ${id.slice(0, 8)}] ${teile}`); +} + export async function loadEmployeeDetail(tx: Tx, id: string, today: string = todayIso()) { + const t0 = performance.now(); // Die Person zuerst, allein: erst aus Eintritt und Austritt ergibt sich der // Stichtag, zu dem ihre Organisation überhaupt eine Antwort hat. Eine // Person, die am 01.09. anfängt, hat heute keine laufende Besetzung — zu @@ -27,6 +41,7 @@ export async function loadEmployeeDetail(tx: Tx, id: string, today: string = tod // behauptete „Keine Führungskraft", obwohl das Team eine hat. const employee = await tx.selectFrom("employees").selectAll().where("id", "=", id).executeTakeFirst(); if (!employee) return null; + const t1 = performance.now(); const asOf = orgAsOf(employee, today); @@ -121,6 +136,8 @@ export async function loadEmployeeDetail(tx: Tx, id: string, today: string = tod ]) .executeTakeFirstOrThrow(); + const t2 = performance.now(); + const orgMaps = buildOrgMaps(g.units as never, g.locations as never); const line = g.lines.find((l) => l.employee_id === id) ?? null; const reports = g.lines.filter((l) => l.acting_manager_id === id); @@ -147,6 +164,15 @@ export async function loadEmployeeDetail(tx: Tx, id: string, today: string = tod resolveOpenPositions(tx, orgMaps, g.open as OffeneStelle[], today), ]); + const t3 = performance.now(); + messen(id, [ + ["person", t1 - t0], + ["hauptabfrage", t2 - t1], + ["nachschlag", t3 - t2], + ["gesamt", t3 - t0], + ["zeilen", g.lines.length], + ]); + return { employee, line,