Doménový hook zapojený jen do části cest — a tichý no-op návazného kroku díru maskuje
import { Aside } from ‘@astrojs/starlight/components’;
Symptom
Sekce “Symptom”U části záznamů, které prokazatelně prošly cílovým stavem (podepsáno, zaplaceno), chyběly odvozené záznamy (v našem případě provize). Bez chyby v logu, bez alertu — přišlo se na to až lidsky („kolega nedostal peníze”).
Root cause
Sekce “Root cause”Dvě vrstvy:
1. Hook visel jen na některých cestách. Generování bylo zapojené do
akce owner_signed na stavové routě a do webhooku e-podpisu. Jenže do
téhož stavu vedly i další reálné cesty — ruční stavová akce complete,
podpis na místě, synchronizační cron — a žádná z nich hook nevolala.
Historie postižených záznamů ukázala, že akce owner_signed u nich
NIKDY neproběhla.
2. Návazný krok mezeru maskoval. Krok „zpřístupni k výplatě” byl podmíněný update:
await db.item.updateMany({ where: { parentId, status: "waiting" }, // žádné řádky neexistují… data: { status: "ready" },}); // …count 0, žádný warn, "OK"Když generace neproběhla, update změnil 0 řádků a tiše prošel — pipeline vypadala zdravě.
Fix
Sekce “Fix”- Jediná služba
ensureDerivedRecords(id): ověří způsobilost, v jedné transakci pod zámkem entity spočítá odvozené záznamy (idempotentně) a hned je posune do správného stavu podle faktů na entitě. Volají ji VŠECHNY cesty k cílovému stavu (stavové akce, webhook, ruční podpis, cron) — hook přestává být vlastnictvím jedné routy. - Observabilita no-opu: pokud podmíněný krok změní 0 řádků a zároveň neexistují ŽÁDNÉ odvozené záznamy, loguje WARN/anomálii — to je signál mezery, ne úspěch.
- Nezpůsobilé případy (např. vlastník s rolí mimo pravidla) → viditelná anomálie, nikdy tichý skip.
Jak se tomu vyvarovat v jiných systémech
Sekce “Jak se tomu vyvarovat v jiných systémech”- Detection: SQL — záznamy v cílovém stavu bez odvozených dat:
SELECT … WHERE stav_dosažen AND NOT EXISTS (odvozené); grep všech zápisových cest k cílovému stavu vs. místa, kde se hook volá. - Anti-pattern: side-effect zavěšený na jméno konkrétní akce místo na
fakt o stavu;
updateManys podmíněným WHERE bez kontroly count. - Lepší přístup: „ensure” semantika (idempotentní, volatelná odkudkoli, bezpečná při dvojím doručení) + počty změněných řádků jako invarianty.