Cache layer
Status: geïntegreerd en lokaal bewezen, 27 augustus 2026. Geen fase-taak in Taken (caching stond nergens gepland); vooruitgetrokken zodat
/api/platform/revalidateuit taak 2.2 straks iets echts te doen heeft.
Twee pogingen
Section titled “Twee pogingen”Eerste poging (26 augustus 2026): hand-rolled generation-counter cache.
Een module-scoped Map in apps/site-template/src/lib/page-cache.ts, een
middleware-hook in servePublicContent(), en invalidateAll()-aanroepen
verspreid over de CMS-acties. Volledig gebouwd, gereviewd (twee bugs
gevonden en gefixt: ongebonden cache-groei via query-strings, een race
tussen een mutatie en een lopende render) en lokaal bewezen met
astro dev.
Ontdekt vlak daarna: dit bestond al, beter. feature/route-cache
(eigen branch, 19 augustus 2026, nooit als PR geopend) loste hetzelfde
probleem op met Astro’s eigen route-cache (cache + memoryCache in
astro.config.mjs, stabiel sinds Astro 7.0 — geverifieerd tegen de
geïnstalleerde astro@7.2.0 in node_modules, niet aangenomen). Die aanpak
wint op meerdere punten tegelijk:
- Zit vóór de middleware. Geverifieerd in de Astro-broncode
(
astro/dist/core/routing/handler.js): de cache-handler krijgt de hele middleware-keten als zijnnext, enmemoryCache’sonRequestroept dienextalleen aan bij een MISS. Een HIT raakt dus geen sessielookup, geen render, geen database — de eigenpage-cache.ts-poging betaalde de sessielookup nog op élke request, ook bij een hit. - Tag-based, niet blanket. Eén CMS-mutatie purge’t precies wat hij
raakt (
rowTag(entity, id),SERVICES_TAG,SITEMAP_TAG, …) in plaats van een generatie-teller die bij élke mutatie de hele cache liet vervallen. - Los de exacte twee bugs op die in de eigen poging gevonden en gefixt
waren — onafhankelijk, want dit is ouder werk: de provider sluit
trackingparameters (
utm_*,fbclid, …) standaard uit van de cache-key en houdt een LRU-cap van 500 entries aan, en de redirect-immutability-bug (zie hieronder) stond er al in gefixt. - Stale-while-revalidate. Na
maxAge(300s) serveert Astro de oude HTML meteen en rendert op de achtergrond opnieuw; een bezoeker wacht nooit op een miss zodra de cache ooit warm is geweest. - Handelt een slugwijziging expliciet af (purge van het oude pad) — de eigen poging deed dit alleen impliciet, via de blanket-invalidatie.
Ontbrekend in de eerste poging, nu wel gedekt: query-string-groei (native
cache: whitelist/LRU in plaats van pathname-only), Set-Cookie-uitsluiting
(native cache doet dit ook, zelfstandig geverifieerd in
memory-provider.js).
Nieuwe kosten van de omschakeling, niet aanwezig in de eerste poging:
astro dev levert onvoorwaardelijk een no-op-cache — geen enkele HIT, ongeacht
config (astro/dist/core/cache/handler.js: runtimeMode === "development" →
NoopAstroCache). Cache-gedrag bewijzen kan dus niet met de snelle dev-loop;
dat vraagt pnpm run build + node dist/server/entry.mjs. Zie
Architectuur § Renderstrategie voor het volledige mechanisme.
Wat er is gebeurd
Section titled “Wat er is gebeurd”page-cache.tsen de bijbehorende middleware-hook/invalidateAll()- aanroepen zijn verwijderd (git checkout --op elk geraakt bestand, want alles stond nog uncommitted).feature/route-cache’ssrc/lib/cache.ts, deastro.config.mjs- cache-config, en decachePublicRoute()/purgePublicCache()/purgeWholeSite()-aanroepen in alle negen publieke pagina’s en acht CMS-actiebestanden zijn overgezet — viagit applyop de zeven bestanden die sindsmainongewijzigd waren, handmatig opcms-media.ts(die hadsecurity/admin-cms-hardeningal herschreven).- De redirect-immutability-fix stond al onafhankelijk in beide takken (zelfde diagnose, zelfde oplossing) — geen samenvoeging nodig.
AGENTS.md(harde regel 6) en Architectuur (§ Renderstrategie, § Beveiliging) bijgewerkt met het native-cache-mechanisme.
Bewijs (27 augustus 2026, gebouwde server, niet astro dev)
Section titled “Bewijs (27 augustus 2026, gebouwde server, niet astro dev)”- MISS → HIT op een verse pagina, identieke body.
?utm_source=fb&fbclid=abc123hit dezelfde entry als de kale URL.admin.localhost:4321/*draagt nooitX-Astro-Cache.- Granulariteit bewezen: na
updatePageop/over-onswerd alleen die pagina een MISS;/bleef een HIT. NacreateNavItem(whole-site purge) werden beide een MISS. Dat is precies het gedrag dat de generation-counter niet kon leveren. - Content na mutatie klopt (nieuwe titel zichtbaar).
- Eigen timing, lokaal, vijf publieke routes: MISS ≈ 7–18 ms, HIT ≈ 1–2,5 ms. Indicatief, geen productiebenchmark.
- Testmutaties teruggedraaid; demo-seed staat weer zoals hij was.
pnpm --filter site-template run typecheckenrun buildgroen.
Buiten scope, wel vastgelegd
Section titled “Buiten scope, wel vastgelegd”/api/platform/revalidate(taak 2.2) kan nupurgeWholeSite(cache)aanroepen in plaats van de eerder geplande no-op-stub — Taakplan 2.2 is hierop bijgewerkt.- Multi-replica cache-consistentie.
memoryCacheleeft in het proces; bij meer dan één replica per tenant mist elke replica deinvalidatevan de andere. Niet relevant bij de huidige schaal (één container per tenant). Zie Architectuur. - Coolify-deploy en de bijbehorende omgeving — nog steeds een los punt, ongewijzigd door deze taak.