142. Žádná obrazovka nečeká na zdroj: stránka ukáže poslední čtení s jeho stářím a čtení ji doběhne zpoza
Kontext
majordomus ui je plocha, kam je člověk posílán jako první (ADR 0135). Vlastník o ní řekl 2026-10-12, poté co byla změřena práce na cachování v dávce výkonu UI: „je tragicky pomalé“, není postaveno na prvotřídní architektuře a má se řešit „architektonicky, streamovaně“, odvozeně, vynucovat přes .ai a být „šíleně rychlé“. Balíček výkonu UI vlastníka už stanovil rozpočty: cachovaná schopnost odpovídá do 50 ms při p95, teplá stránka renderovaná serverem do 100 ms, živá aktualizace do 250 ms.
Změřeno téhož dne na vlastním buildu dávky (perf/the-ui-answers-at-once na c66f49e406, debug build na stroji se zátěží 20; release build je třikrát až pětkrát rychlejší a tvar je stejný):
| Cesta | Každý požadavek | První požadavek po startu nebo změně |
|---|---|---|
/ui | 2.2-2.5 s | 2.3 s |
/ui/roadmap | 0.08-0.12 s | 8.1 s |
/ui/evidence | 0.07-0.09 s | 5.6 s |
/ui/governance | 0.07-0.08 s | 6.0 s |
/ui/panels/delivery | neměřeno | 75.7 s |
/cockpit | 6.1-6.3 s | 7.0 s |
Cache dávky funguje tam, kde se uplatní. Zbývají tři věci a žádná cache je neodstraní:
- Stránka stojí tolik, co její nejpomalejší zdroj.
health.reportspouští sondy a nesmí se cachovat déle než pár vteřin. Je v prvním vykreslení dashboardu, takže na ni čeká každý čtenář/ui. - Cache je prázdná přesně tehdy, když se člověk podívá. Položka je klíčována stavem repozitáře (
CachePolicy::Generation,Checkout). Tento repozitář se mění celý den, takže první čtenář po každé změně platí celou cenu.project.cache-is-invisiblecache zakazuje odpovídat čímkoli jiným. - Server neumí levně držet spojení otevřené. Každý požadavek běží na vlastním vlákně, nejvýše 64 najednou, a odpověď se zapisuje celá s délkou (
http/server.rs). UI proto dotazuje každou minutu. Stream na každou otevřenou stránku by spotřeboval handlery, které požadavky potřebují.
První dva jsou jedna vada: požadavek počítá. Cache byla požádána, aby skryla výpočet, který na cestě požadavku vůbec být neměl.
Rozhodnutí
Obrazovka nikdy nečeká na zdroj. Ukáže poslední čtení každého zdroje hned, s údajem, jak je staré, a čtení se za čtenářem dožene. Čtyři části, každá jako vlastní úkol.
- Poslední čtení, vedle cache (I2486). Exekutor si pamatuje poslední, co každý zdroj odpověděl, a kdy (
capability::readings). Stránka žádá o čtení, nikdy o odpověď.- Zdroj čtený v repozitáři tak, jak je teď, se předává z paměti. Na cestě požadavku se nedotkne žádný handler, git ani soubor.
- Čtení starší než sekunda se čte znovu pracovníkem za volajícím.
- Když se repozitář pohnul, volající čeká na nové čtení nejvýše 50 ms. Po uplynutí dostane staré, označené jako zastaralé a „reading again“. Každý jiný nedávno čtený zdroj se ve stejném okamžiku přečte znovu.
- Na nikdy nepřečtený zdroj se čeká, protože není co zobrazit místo něj, a po minutě se označí jako čekající.
- Zdroj, jehož odpověď cache exekutoru už pro přesunutý repozitář drží, je aktuální okamžitě, bez workeru.
- Na vlastní reload repozitáře se také nečeká. Stránka se vykreslí z existující generace, řekne, že se repozitář změnil, a načte se znovu, jakmile se za ní načte další generace (
Live::view_without_waiting).Live::view, které používá každá odpověď, stále čeká na generaci, kterou volající viděl. executezůstává nedotčeno. Odpověď přes API, MCP nebo příkazový řádek je stále přesná aproject.cache-is-invisiblestále platí. Čtení je něco jiného a vždy říká, čím je.- První čtenář UI spustí jeden průchod na pozadí přes
SCREENSaPANELS, seznamy, k nimž je navigace už vázána, takže další obrazovka je připravena dřív, než se otevře. Neexistuje žádný druhý seznam zdrojů.
- Streamovaná stránka a jeden proud změn (I2487). Shell a každý panel, jehož čtení je po ruce, se posílají najednou. Panel, jehož první čtení čeká, dorazí ve stejné odpovědi, až bude připraven, v pořadí dokumentu, takže stránka je kompletní bez skriptu. Poté jedno předplatné WebSocket na modelu událostí ADR 0033 sděluje stránce, která čtení se změnila, a stránka tyto panely nahradí. Minutové dotazování a doplňkové znovupřečtení z části 1 odpadají.
- Jeden HTTP stack, zvolený měřením (I2488). Dlouho žijící streamy jsou to, co současný stack neudrží. Stejné trasy, jedna streamovaná trasa a několik stovek nečinných odběratelů se měří na
tiny_httpa naaxumpřestokio, harnessem pro bench, na nečinném hostu. Výsledek je samostatný záznam rozhodnutí. Repozitář drží jeden HTTP stack, nikdy dva. - Rozpočty jsou kontrola (I2489). Rozpočty vlastníka se stávají klíči politiky, bench každou obrazovku proti nim měří a CI odmítne obrazovku nad rozpočtem. Sken odmítne obrazovku, která volá handler místo vyžádání čtení.
Důsledky
- Čtenář může na okamžik vidět hodnotu z doby před poslední změnou. Říká to: řádek jejího zdroje čte „read 12 s ago, before the repository changed; reading again“ a stránka ji nahradí, když přistane nové čtení. Zastaralá hodnota se nikdy nevykresluje jako aktuální.
- Řádek zdroje získá stav. Aktuální čtení starší než pět sekund říká svůj věk.
- Zdroje UI se čtou znovu, kdykoli se repozitář pohne, ať se někdo dívá, či ne, po dobu pěti minut po posledním čtenáři. To je práce na pozadí až na čtyřech vláknech, omezená tím, co čtenáři skutečně otevřeli.
MAJORDOMUS_CAPABILITY_CACHE=offzpůsobí, že čtení spustí svůj handler a počká, jako každé jiné volání.MAJORDOMUS_READING_PATIENCE_MS=waitponechá úložiště a způsobí, že každé čtení čeká na aktuální čtení, pro test, který změní repozitář a poté čte stránku.- Stránky Cockpitu se stále počítají při požadavku. Přesunout je na čtení je tatáž změna a následuje po změně UI.
Odmítnuté alternativy
- Dlouhověkější cache. Odpovídala by hodnotou z jiného stavu repozitáře a nazvala to odpovědí, což
project.cache-is-invisibleodmítá. - Nejdřív změnit HTTP stack. Naměřená cena je v handlerech, ne v transportu: cachovaná schopnost už odpovídá pod 6 ms na p95. Rychlejší transport by přinesl stejné osmisekundové čekání.
- Klientská aplikace nad JSON API. Stránka by už bez skriptu nebyla úplná, existovalo by druhé vykreslení každého panelu a ADR 0012 odmítá povrch, který je aplikací místo projekce.
Stav
Navrhla relace p12 dne 2026-10-12 na slova vlastníka, dohodnuto s p8, který drží balíček výkonu UI. Část 1 je implementována s tímto záznamem. Přijetí je čin vlastníka.