Die Messung wieder entfernen -- die Ursache steht fest
All checks were successful
CI / Lint, Typen, Tests, Build (push) Successful in 11m17s
CI / Migrationen auf leerer Datenbank (push) Successful in 10m13s

Die Akte oeffnet jetzt in rund 100 ms statt in 1,5 s:

  vorher   person 4ms, hauptabfrage 1554ms, nachschlag 11ms, gesamt 1569ms
  nachher  person 2ms, hauptabfrage  79ms, nachschlag  7ms, gesamt   87ms

Gefunden hat das der vorlaeufige console.info in dieser Datei, nicht das
Nachdenken darueber. Behoben haben es zwei Migrationen: die Policies
pruefen die Rechte einmal je Abfrage (20260924200000), und
om_reporting_lines laeuft als security definer mit require_hr_admin in
der ersten Zeile (20260924220000).

Nebenbei die Behauptung im Kopf der Datei richtiggestellt: dort standen
"rund 36 ms Umlaufzeit" als Begruendung dafuer, die Zahl der Rundreisen
zu druecken. Nachgemessen sind es 0,5 ms. Die Rundreisen waren nie das
Problem.
This commit is contained in:
2026-09-24 12:39:50 +02:00
parent 520242e3c3
commit e11544b8da

View File

@@ -10,8 +10,13 @@ import { offeneStellenAbfrage, resolveOpenPositions, type OffeneStelle } from ".
//
// Vorher: die Person, dann acht Dinge in einem Promise.all, dann die Namen der
// Beteiligten. Das Promise.all war keine Gleichzeitigkeit — eine Transaktion
// hängt an einer Verbindung (lib/db/json.ts). Bei rund 36 ms Umlaufzeit war
// diese Seite damit die teuerste der Anwendung.
// hängt an einer Verbindung (lib/db/json.ts).
//
// Hier stand „bei rund 36 ms Umlaufzeit". Nachgemessen sind es 0,5 ms; die
// Zahl der Rundreisen ist also nicht der Hebel, für den sie hier gehalten
// wurde. Teuer war die Akte aus einem anderen Grund — 1,5 s, davon 1,4 s in
// om_reporting_lines unter Zeilenschutz (siehe Migration 20260924220000).
// Jetzt liegt die ganze Seite bei rund 100 ms.
//
// Die drei Tabellen mit `selectAll()` stehen hier ausgeschrieben. Das ist
// keine Umständlichkeit: innerhalb von JSON formatiert Postgres timestamptz
@@ -19,21 +24,7 @@ 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
@@ -41,8 +32,6 @@ 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);
const g = await tx
@@ -136,8 +125,6 @@ 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);
@@ -164,15 +151,6 @@ 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,