La mise en cache inclut
Inclut leur propre cycle de vie de cache indépendant de la page. Déclarez une durée de vie sur l'élément et bext stocke le code HTML résolu dans son cache hiérarchisé ; les requêtes suivantes sont diffusées à partir du cache sans appeler le résolveur.
<bext-include src="/__bext/nav"
ttl="5m" swr="1h" tags="chrome"
></bext-include>
La navigation est résolue une fois, servie fraîche pendant 5 minutes, puis servie rassis pendant une heure maximum tandis que bext la revalide en arrière-plan. Marqué chrome afin que vous puissiez invalider tous les fragments Chrome avec un seul appel de purge.
Les attributs#
| Attribut | Format | But |
|---|---|---|
ttl |
5m, 1h, adaptive |
Durée de vie de la fraîcheur du cache |
swr |
1h, 1d |
Fenêtre obsolète pendant la revalidation |
tags |
chrome,nav |
Balises pour invalidation groupée |
Syntaxe de durée : 30s, 5m, 1h, 1d ou ms brut (par exemple, 200ms ). Non ttl signifie pas de mise en cache.
Comment fonctionne la mise en cache#
Le cache d'inclusion de bext est un wrapper sur le cache hiérarchisé existant :
Request arrives
↓
Compute cache key = hash(src, vary_values)
↓
L0 (hot map) → HIT: return cached HTML
→ MISS ↓
L1 (mmap) → HIT: promote to L0, return
→ MISS ↓
L2 (Redis) → HIT: promote to L0+L1, return
→ MISS ↓
Resolver chain → compute HTML
↓
Store in L0 + L1 + L2 with TTL
↓
Return HTML
- L0 est un DashMap par processus – lectures inférieures à la microseconde.
- L1 est sauvegardé par mmap et persiste lors des redémarrages, de sorte que les caches chauds survivent aux déploiements.
- L2 est Redis (facultatif) et partage l'état sur des instances à l'échelle horizontale.
Obsolète pendant la revalidation#
Avec swr="1h" :
time=0 include resolved, stored in cache (TTL=5m, SWR=1h)
time=4m request → fresh → return cached
time=6m request → stale but in SWR window
→ return cached HTML immediately (non-blocking)
→ spawn background task to revalidate
background task completes → cache updated
time=1h5m request → expired → cache miss → resolve
L'utilisateur n'attend jamais la revalidation. Le Stampede Guard garantit une seule revalidation en arrière-plan par clé, même en cas de pics de trafic.
Balises de cache#
Les balises vous permettent d'invalider des groupes d'inclusions à la fois :
<bext-include src="/__bext/nav" tags="chrome,nav"></bext-include>
<bext-include src="/_partials/footer.html" tags="chrome"></bext-include>
Après avoir déployé un nouveau pied de page :
curl -X POST http://localhost/__bext/cache/purge \
-d '{"tags":["chrome"]}'
Les deux inclusions sont invalidées. La requête suivante les reconstruit.
Les balises peuvent également provenir de la configuration :
[[include.rules]]
pattern = "/__bext/nav*"
tags = ["chrome", "nav"]
[[include.rules]]
pattern = "/_partials/*"
tags = ["chrome"]
TTL adaptatif#
Utilisez ttl="adaptive" pour adapter la durée de vie du cache en fonction du nombre d'accès :
<bext-include src="/partials/homepage-hero"
ttl="adaptive" swr="1h" tags="homepage"
></bext-include>
La formule (identique au TTL adaptatif ISR) :
next_ttl = min(base_ttl * (1 + ln(1 + hits)), max_ttl)
Avec les valeurs par défaut (base=60s, max=600s, seuil=10 hits) :
| Nombre de coups | TTL efficace |
|---|---|
| < 10 | Années 60 (base) |
| 100 | ~337s |
| 1000 | ~475s |
| 10000 | 600s (plafonné) |
| ∞ | 600s (plafonné) |
Les options les plus populaires incluent rester en cache plus longtemps (meilleur amortissement du coût de résolution). Les inclusions froides expirent à la base TTL (meilleure fraîcheur pour la longue traîne).
Protection contre la bousculade#
Lorsqu'une inclusion à chaud expire et qu'un flot de requêtes simultanées arrive, un seul travailleur se régénère — les autres servent du contenu obsolète (si dans la fenêtre SWR) ou attendent la fin de la première régénération.
pub enum StampedeLock {
Acquired(token), // you regenerate
Waiting, // someone else is regenerating
}
Cela évite le problème du « troupeau tonitruant » où N travailleurs calculent tous simultanément le même fragment coûteux.
Configuration#
Réglage du cache global :
[include.cache]
enabled = true
l1_max_entries = 5000
l2_prefix = "bext:include:"
max_variants_per_include = 100
[include.cache.adaptive]
base_ttl_ms = 60_000
max_ttl_ms = 600_000
extension_threshold = 10
Valeurs par défaut par source (appliquées à toute inclusion correspondante, y compris SSI/ESI) :
[[include.rules]]
pattern = "/__bext/nav*"
ttl = "5m"
swr = "1h"
tags = ["chrome"]
[[include.rules]]
pattern = "/api/*"
ttl = "30s"
swr = "5m"
timeout = "200ms"
[[include.rules]]
pattern = "/_partials/*"
ttl = "1d"
tags = ["chrome"]
La première règle correspondante gagne. Les attributs de l'élément HTML remplacent la règle.
La première entrée [[include.rules]] correspondante gagne. Placez des modèles plus spécifiques (par exemple /api/live/* ) avant les modèles fourre-tout généraux (par exemple /api/* ) afin qu'ils ne soient pas ombragés.
Quand ne pas mettre en cache#
L'omission de ttl désactive entièrement le cache : le résolveur s'exécute à chaque requête. Pour les données en direct, préférez un court ttl plus swr, ou utilisez Le temps réel inclut au lieu de compter sur la mise en cache zéro TTL.
Certaines inclusions devraient être résolues à chaque demande :
- Personnalisation par utilisateur sans dimension variable (vous mettriez en cache le contenu d'un utilisateur pour un autre).
- Données en direct telles que les horodatages actuels, les prix des crypto-monnaies et les cotations boursières. Utilisez plutôt
realtime. - Opérations ponctuelles comme les jetons CSRF, les noms occasionnels.
Omettez ttl pour contourner complètement le cache : le résolveur s'exécute à chaque requête.
Voir aussi#
- Mise en cache adaptée aux variations —
vary="role,locale"pour la mise en cache par dimension - Délai d'attente et repli — résilience pour les sources lentes
- Le temps réel inclut — mises à jour en direct au lieu de courtes durées de vie
- API de purge du cache — Invalidation basée sur les balises de bext