Mise en cache
bext comprend un système de mise en cache multicouche conçu pour les applications rendues par le serveur. Prêt à l'emploi, il fournit une ISR (régénération statique incrémentielle) avec revalidation en arrière-plan, une protection contre les bousculades pour empêcher les troupeaux tonitruants et des clés de cache sensibles aux variations. Vous pouvez ajouter un niveau Redis L2 pour une mise en cache distribuée sur plusieurs instances.
Cache ISR (TTL + périmé pendant la revalidation)#
Le cache ISR stocke le rendu HTML avec une durée de vie et une fenêtre de revalidation obsolète (SWR). Tant que la réponse se trouve dans sa durée de vie, elle est servie directement à partir du cache. Une fois la durée de vie expirée mais la fenêtre SWR est toujours ouverte, bext diffuse immédiatement le contenu obsolète et déclenche une revalidation en arrière-plan pour actualiser l'entrée.
[cache.isr]
max_entries = 10000 # LRU eviction when exceeded
default_ttl_ms = 60000 # 60 seconds fresh
default_swr_ms = 3600000 # 1 hour stale-while-revalidate window
Vous pouvez remplacer TTL et SWR par route à l'aide des règles de route :
[[route_rules]]
pattern = "/blog/*"
render = "isr"
ttl_ms = 300000 # 5 minutes
swr_ms = 86400000 # 24 hours
[[route_rules]]
pattern = "/dashboard/*"
render = "ssr"
cache = false # Disable caching entirely
La chronologie d'une réponse mise en cache ressemble à ceci :
|-- TTL (fresh) --|-- SWR (stale, revalidating) --|-- expired --|
serve cache serve stale + background re-render
revalidation
Invalidation des balises de cache#
Chaque réponse mise en cache peut contenir des balises : des chaînes arbitraires qui regroupent les entrées associées. Lorsque vos données changent, invalidez-les par balise au lieu de vider tout le cache.
Les balises sont définies via l'en-tête de réponse X-Cache-Tags de votre application :
X-Cache-Tags: product:42, category:electronics, homepage
Invalidez une balise via l'API d'administration :
curl -X POST http://localhost:3061/__bext/cache/invalidate \
-H "Content-Type: application/json" \
-d '{"tag": "product:42"}'
Cela purge chaque entrée en cache marquée avec product:42 de L1 et L2 (lorsque Redis est configuré). En mode Edge, l'invalidation est diffusée via Redis Pub/Sub à toutes les instances.
Cache de bundles à la demande#
Lors de l'utilisation du rendu à la demande PRISM (V8), les groupes de routes compilés sont mis en cache à trois niveaux :
| Niveau | Emplacement | Survit au redémarrage | Annulation |
|---|---|---|---|
| Mémoire | DashMap (par processus) | Non | Changement d'heure source |
| Disque | .bext/ssr-cache/{route}.js |
Oui | Changement d'heure source |
| Compiler | Sous-processus de chignon | — | Toujours frais |
À la première demande, bext compile l'itinéraire via Bun et le met en cache en mémoire et sur disque. Après un redémarrage du serveur, les routes se chargent à partir du cache disque sans compilation Bun. Le cache disque est invalidé lorsque les heures du fichier source changent.
Le bundle de base React (747 Ko) et le shell du site (31 Ko) sont également mis en cache sur disque sur .bext/v8-react-base.js et .bext/ssr-cache/__shell.js.
Configurez la durée de vie du cache à la demande :
[nextjs]
cache_ttl = 60 # Response cache TTL in seconds
cache_swr = 300 # Stale-while-revalidate window in seconds
Stampede Guard (Revalidation fusionnée)#
Lorsqu'une page populaire expire, des centaines de requêtes simultanées peuvent toutes déclencher un nouveau rendu simultanément - le problème du troupeau tonitruant. La protection contre la bousculade de bext garantit que seule une seule requête effectue la revalidation. Toutes les autres demandes simultanées pour la même clé de cache reçoivent la réponse obsolète et attendent la fin du seul travailleur en arrière-plan.
Le pool de revalidation est configurable :
[cache.revalidation]
pool_size = 4 # Background worker threads
max_queue_size = 1000 # Max pending revalidation requests
dedup = true # Deduplicate by cache key (default)
Avec dedup = true (valeur par défaut), si 200 requêtes arrivent pour /products/42 pendant sa revalidation, une seule revalidation est exécutée. Les 199 autres requêtes concernent la version obsolète en cache.
Cache hiérarchisé L1 + L2#
Avec Redis configuré, bext exploite un cache à deux niveaux :
| Étage | Back-end | Latence | Portée |
|---|---|---|---|
| L1 | DashMap en mémoire | Sous-microseconde | Par instance |
| L2 | Redis | ~1 ms RTT réseau | Partagé dans toute la flotte |
[redis]
url = "redis://localhost:6379"
prefix = "bext:"
[cache.tiered]
write_through = true # Sync write to L2 on every cache set
promote_on_l2_hit = true # Copy L2 hits into L1 for next request
L'ordre de recherche est : L1 -> L2 -> rendu. Lors d'un accès L2, l'entrée est promue en L1 afin que les requêtes suivantes soient traitées à la vitesse de la mémoire. Les écritures vont à la fois vers L1 et L2 lorsque write_through est activé.
Si Redis devient indisponible, bext se dégrade progressivement : L1 continue de fonctionner et les nouveaux rendus sont mis en cache localement. La reconnexion est automatique.
Les statistiques du cache sont exposées via le point de terminaison des métriques Prometheus :
bext_cache_l1_hits_total
bext_cache_l2_hits_total
bext_cache_l2_promotes_total
bext_cache_misses_total
bext_cache_l2_write_errors_total
Mise en cache négative#
Les réponses d'erreur (404, 429, 502, 503) sont mises en cache pendant de courtes durées afin d'éviter d'atteindre à plusieurs reprises le pipeline SSR pour des pages connues pour être interrompues ou dont le débit est limité.
[cache.negative]
enabled = true
max_entries = 5000
[cache.negative.ttl_by_status]
404 = 30000 # 30 seconds
429 = 5000 # 5 seconds
502 = 10000 # 10 seconds
503 = 5000 # 5 seconds
Chaque code d'état possède son propre TTL configurable. Seuls les codes d'état répertoriés dans ttl_by_status sont éligibles pour la mise en cache négative : une erreur 500 n'est pas mise en cache par défaut car elle indique généralement un bogue passager.
Clés de cache sensibles aux variations#
Les réponses HTTP peuvent varier en fonction des en-têtes de requête tels que Accept-Encoding ou Accept-Language. bext respecte l'en-tête Vary et intègre les valeurs d'en-tête de requête pertinentes dans la clé de cache, afin que différentes représentations soient stockées et servies correctement.
[cache.key]
include_query = true # Include query string (default)
include_headers = ["accept-language"] # Extra headers for cache key
ignore_query_params = ["utm_source", "utm_medium", "fbclid"]
include_cookies = ["currency", "region"] # Cookie values in cache key
Les paramètres de suivi ( utm_*, fbclid, gclid, etc.) sont supprimés des clés de cache par défaut afin que les URL ne différant que par les paramètres d'analyse partagent une seule entrée de cache. Vous pouvez ajouter des paramètres supplémentaires à supprimer via ignore_query_params.
Lorsqu'une réponse inclut Vary: *, bext contourne entièrement le cache pour cette réponse, car le caractère générique indique que le contenu varie en fonction de facteurs imprévisibles.
Isolation du cache des locataires#
Dans les applications multi-locataires, le cache de chaque locataire est doté d'un espace de noms pour éviter les fuites de données entre locataires :
[cache.tenant]
ttl_ms = 300000 # 5 minutes
L'identifiant du locataire est extrait de la demande (via la revendication JWT, le sous-domaine ou l'en-tête) et inclus automatiquement dans la clé de cache.
Lorsqu'une réponse inclut Vary: *, bext contourne entièrement le cache pour cette réponse : elle ne peut pas être stockée. Évitez d'émettre Vary: * depuis votre application, sauf si le contenu varie réellement en fonction de facteurs imprévisibles.
En rapport#
- Modes de déploiement — mode Edge avec Redis L2 et invalidation distribuée
- Routage — remplacements par itinéraire
render,ttl_msetswr_msvia[[route_rules]] - API de purge du cache — le point de terminaison de purge utilisé par les plugins et l'intégration WordPress
- Compagnon : Explorateur de cache : inspectez les entrées du cache dynamique et déclenchez des invalidations à partir de l'interface utilisateur
- Comprend : Mise en cache — primitives de mise en cache disponibles dans les inclusions du serveur et les fragments SSI/ESI