3.15

Voir en anglais

3.15 Mise en cache et diffusion de contenu

Vue d’ensemble et motivation

Un cache est une copie de données gardée quelque part plus rapide ou plus proche que l’original, pour pouvoir répondre à une requête sans refaire le travail complet et coûteux. Presque chaque système qui semble rapide est rapide à cause de la mise en cache. La requête de base de données qui prendrait 40 millisecondes revient en moins d’une quand son résultat se trouve déjà en mémoire. L’image qui traverserait un océan est servie depuis une machine dans la même ville. La mise en cache est la technique de performance au levier le plus élevé que vous ayez, et c’est aussi celle la plus susceptible de vous remettre un bug subtil et exaspérant.

Ce chapitre va en profondeur sur la stratégie de mise en cache. Le chapitre 3.4 (architecture et stockage des données) introduit les caches et les réseaux de diffusion de contenu comme une préoccupation de stockage parmi d’autres, et le chapitre 3.13 (réseaux et connectivité) couvre le chemin réseau sur lequel ils roulent. Ici vous obtenez les décisions : où placer un cache, comment le nommer par clé, quand l’invalider, comment le protéger sous charge, et comment raisonner sur la péremption que vous échangez contre la vitesse. La mise en cache touche l’ingénierie de performance (chapitre 2.16), l’évolutivité et la résilience (chapitre 3.5), les réalités de panne partielle des systèmes distribués (chapitre 3.3), et, parce qu’un cache empoisonné peut servir une attaque à des milliers d’utilisateurs, la sécurité applicative (chapitre 4.2).

La motivation se résume à trois leviers. La mise en cache réduit la latence, donc les utilisateurs attendent moins. Elle réduit la charge, donc votre origine sert plus de trafic sur le même matériel. Et elle réduit le coût, parce qu’une requête répondue à la périphérie ne touche jamais votre base de données, votre calcul, ou votre facture de sortie. Pour les grandes équipes, une stratégie de mise en cache partagée est la différence entre une plateforme qui s’échelonne de façon prévisible et une où chaque service réinvente l’invalidation et se trompe. Dans les systèmes d’entreprise et gouvernementaux, où le trafic pique aux échéances de dépôt et aux jours de lancement, un cache bien conçu est souvent ce qui se trouve entre un portail fonctionnel et un échec public.

Principes clés

  • Mettez en cache pour réduire la latence, la charge, et le coût, et sachez lequel vous achetez.
  • Placez les caches à la bonne couche de la hiérarchie, au plus près d’où ils aident le plus.
  • Traitez l’invalidation comme la partie difficile ; concevez les clés et durées de vie avant de mettre en cache.
  • Protégez le cache sous charge avec la coalescence, la gigue, et les défenses contre les ruées.
  • Choisissez un motif d’écriture délibérément : la cohérence et la vitesse tirent l’une contre l’autre.
  • Mesurez le taux de succès, la péremption, et la charge d’origine ; un cache non mesuré est un passif.
  • Traitez le contenu mis en cache comme une surface d’attaque ; un cache empoisonné sert tout le monde.

Recommandations

Comprendre la hiérarchie de cache

La mise en cache n’est pas une chose à un endroit. C’est une hiérarchie de copies, chacune plus proche de l’utilisateur que la précédente, et vous concevez à travers toute cette hiérarchie. Le plus proche de l’utilisateur est le cache client : le cache HTTP du navigateur, le magasin local d’une application mobile, un cache mémoire en processus. Ensuite le réseau de diffusion de contenu (CDN), une flotte de serveurs distribués à travers le monde qui détiennent des copies de votre contenu à la périphérie du réseau, près des utilisateurs. Derrière cela se trouve le cache de proxy inverse ou de passerelle, un cache partagé devant vos serveurs. Puis le cache d’application : un magasin clé-valeur rapide tel qu’une grille de données en mémoire détenant des résultats calculés, des sessions, et des fragments rendus. Enfin le propre cache de requête et tampon de la base de données, qui garde les pages chaudes en mémoire pour que le disque soit touché moins souvent.

Chaque couche sert un travail distinct : le cache client élimine entièrement la requête, le CDN absorbe le trafic de lecture mondial, le proxy inverse protège votre origine du travail identique répété, le cache d’application économise le recalcul, et le cache de base de données garde le magasin réactif. Une requête qui manque chaque couche et atteint la base de données est le chemin le plus lent et le plus coûteux que vous ayez, donc le but de la hiérarchie est de répondre aussi loin en amont et en dehors que vous le pouvez en sécurité. Concevez-la comme un système, parce qu’un fragment mis en cache à la couche application et une copie CDN périmée au-dessus peuvent être en désaccord de façons qui confondent les utilisateurs.

Traiter l’invalidation comme le problème difficile

Il y a une vieille blague selon laquelle les deux problèmes les plus difficiles en informatique sont nommer les choses, l’invalidation de cache, et les erreurs de décalage d’un. La blague perdure parce que l’invalidation est véritablement difficile : un cache est une copie, et à l’instant où l’original change, chaque copie est un mensonge potentiel. Vous avez trois stratégies larges. L’expiration basée sur le temps avec une durée de vie (TTL), la durée pendant laquelle une entrée reste valide avant d’être considérée périmée, est la plus simple : vous acceptez une péremption bornée et laissez les entrées vieillir. L’invalidation explicite purge ou met à jour les entrées quand les données sous-jacentes changent, ce qui est précis mais exige que vous sachiez chaque endroit où vit une copie. L’invalidation pilotée par événements abonne les caches à des événements de changement pour qu’ils se rafraîchissent eux-mêmes, ce qui s’échelonne mieux à travers de nombreux caches mais ajoute une dépendance de messagerie.

La plupart des systèmes réels mélangent ceux-ci : des TTL courts pour les données qui changent souvent et tolèrent des secondes de péremption, des TTL plus longs plus une purge explicite pour les données qui changent rarement mais doivent être correctes quand elles le font, et des clés de cache versionnées pour le contenu qui est immuable une fois publié. L’astuce de clé versionnée vaut la peine d’être internalisée : au lieu d’invalider, vous changez la clé. Une feuille de style servie comme app.v187.css n’a jamais besoin de purge, parce qu’une nouvelle version est une nouvelle clé et l’ancienne cesse simplement d’être demandée. Chaque fois que vous pouvez transformer un problème d’invalidation en problème de nommage, faites-le.

Concevoir délibérément les clés de cache et les TTL

Un cache ne vaut que ce que vaut sa clé. La clé de cache est l’identifiant sous lequel une valeur est stockée et recherchée, et se tromper cause deux échecs opposés. Trop grossière, et vous servez les données d’un utilisateur à un autre : une page personnalisée mise en cache sous une URL qui ignore l’identité utilisateur est une fuite de données. Trop fine, et votre taux de succès s’effondre parce qu’aucune deux requêtes ne partagent une clé. Décidez délibérément ce qui appartient à la clé : l’identité de ressource plus tout ce qui varie légitimement la réponse (langue, devise, classe d’appareil) et rien qui ne le fait pas. Normalisez les clés pour que des différences triviales comme l’ordre des paramètres de requête ne fragmentent pas le cache.

Les TTL méritent la même réflexion. Un TTL est une promesse sur la péremption maximale que vous servirez, donc fixez-le à partir de la vraie tolérance des données, pas d’un chiffre rond que quelqu’un a deviné : un ticker boursier tolère des secondes, un catalogue de produits des minutes, une réglementation publiée des heures ou une clé versionnée et aucune expiration du tout. Ajoutez une petite dispersion aléatoire, appelée gigue, pour qu’un lot d’entrées écrites ensemble n’expirent pas toutes au même instant et ne fassent pas une ruée sur l’origine. Consignez ces choix par écrit, parce qu’un TTL sans justification est un chiffre que le prochain ingénieur aura peur de changer.

Se protéger contre les ruées et coalescer les requêtes

Quand une entrée de cache populaire expire, chaque requête qui la voulait manque à la fois et se précipite ensemble sur l’origine. C’est la ruée de cache, aussi appelée le troupeau tonitruant, et elle peut renverser la base de données même que le cache protégeait. Construisez les défenses une fois et réutilisez-les partout. La coalescence de requêtes (single-flight) laisse seulement la première requête pour une clé manquante recalculer la valeur pendant que les autres attendent son résultat, donc mille manqués simultanés causent un appel d’origine. Le recalcul anticipé probabiliste rafraîchit une entrée chaude aléatoirement légèrement avant qu’elle n’expire, donc une requête en arrière-plan la renouvelle avant que la foule ne voie un manqué. Une politique stale-while-revalidate sert la copie légèrement périmée immédiatement et la rafraîchit de façon asynchrone, donc les utilisateurs n’attendent jamais sur un manqué du tout.

Ces motifs comptent le plus exactement quand vous avez le plus besoin du cache, sous charge de pointe, donc validez-les à une échelle réaliste : une défense qui fonctionne avec dix utilisateurs peut encore échouer à dix mille. Associez-les aux motifs de résilience du chapitre 3.5, spécialement les délais d’expiration et les disjoncteurs, pour que quand l’origine est vraiment lente votre couche de cache la protège plutôt que d’ajouter à la charge. Le but est un cache qui se comporte le mieux sous pression, pas un qui amplifie un pic en panne.

Choisir délibérément un motif d’écriture

Comment vous gérez les écritures décide à quel point votre cache reste frais et combien vous risquez en cas de panne. Il y a quatre motifs courants. Dans le cache-aside (chargement paresseux), l’application vérifie le cache, et sur un manqué lit l’origine, remplit le cache, et renvoie la valeur ; les écritures vont à l’origine et invalident l’entrée. C’est la valeur par défaut pour de bonnes raisons : simple, et le cache ne détient que ce qui est demandé. Dans le write-through, chaque écriture va au cache et à l’origine ensemble, donc le cache est toujours actuel, au coût de la latence d’écriture et de la mise en cache de données qui ne seront peut-être jamais lues. Dans le write-back (write-behind), les écritures touchent le cache d’abord et sont vidées vers l’origine de façon asynchrone, rendant les écritures rapides mais risquant une perte si le cache meurt avant le vidage. Dans le write-around, les écritures vont directement à l’origine et sautent le cache, évitant le renouvellement du cache par des données lourdes en écriture rarement lues, au coût d’un manqué garanti à la première lecture.

Choisissez par charge de travail, pas une fois pour tout le système. Un catalogue lourd en lecture convient au cache-aside ou write-through. Un journal ou flux de métriques lourd en écriture convient au write-around, pour que le cache ne soit pas renouvelé par des données que personne ne relit. Le write-back convient aux écritures à haut débit où un petit risque compris de perte est acceptable et la durabilité est gérée ailleurs. Énoncez explicitement le motif pour chaque cache, parce qu’un lecteur qui suppose cache-aside quand le code fait write-back jugera mal à la fois la fraîcheur et le comportement d’échec.

Assortir la politique d’éviction à votre motif d’accès

Un cache a une taille fixe, donc quand il se remplit, quelque chose doit partir. La politique d’éviction décide quoi. Le moins récemment utilisé (LRU) évince l’entrée non touchée depuis le plus longtemps, pariant que l’usage récent prédit l’usage futur, et c’est une valeur par défaut sensée. Le moins fréquemment utilisé (LFU) évince l’entrée avec le moins de succès, ce qui convient aux ensembles chauds stables où quelques éléments sont perpétuellement populaires mais peut s’accrocher à des entrées qui étaient chaudes autrefois et ne s’adapte jamais. Des variantes telles que LRU segmenté et des politiques adaptatives mélangent récence et fréquence ; premier-entré-premier-sorti et l’expiration simple basée sur le temps sont moins chères mais plus grossières.

Assortissez la politique à comment vos données sont accédées : LFU ou une politique consciente de la fréquence pour un petit ensemble chaud qui change rarement, LRU là où la popularité bouge dans le temps comme avec l’actualité ou le contenu tendance. Quel que soit votre choix, dimensionnez le cache pour que l’ensemble chaud tienne, parce qu’un cache trop petit pour contenir l’ensemble de travail thrashe, évinçant des entrées juste avant qu’elles ne soient nécessaires à nouveau. Surveillez le taux d’éviction comme métrique de premier ordre, puisqu’une hausse soudaine signifie généralement que le cache est sous-dimensionné ou qu’une explosion de clés le fragmente.

Utiliser correctement la sémantique de mise en cache HTTP

Le web a un modèle de mise en cache mature et standardisé intégré à HTTP, et bien l’utiliser vous donne la mise en cache client et CDN gratuitement. L’en-tête Cache-Control est la surface de contrôle : max-age fixe la durée de vie de fraîcheur, public et private disent si les caches partagés peuvent stocker la réponse, no-store interdit la mise en cache, et stale-while-revalidate permet de servir une copie périmée pendant le rafraîchissement. La validation permet à un cache de vérifier la fraîcheur à bas coût sans re-récupérer le corps. Un ETag (balise d’entité) est un identifiant de version opaque que le serveur attache à une réponse ; le client le renvoie dans un en-tête If-None-Match, et le serveur répond 304 Not Modified sans corps si rien n’a changé. Last-Modified avec If-Modified-Since fait la même chose en utilisant des horodatages.

La discipline pratique est d’être explicite. Fixez Cache-Control sur chaque réponse plutôt que de laisser les caches deviner avec des heuristiques. Marquez les réponses privées et par utilisateur private ou no-store pour qu’un proxy partagé ne les stocke jamais, une erreur courante et dangereuse. Utilisez des URL versionnées avec un long max-age et la directive immutable pour les actifs statiques, et la validation avec des ETags pour le contenu qui change de façon imprévisible. Bien faire ces en-têtes transforme tout le palier client et CDN en un cache correct et fondé sur des normes que vous n’avez pas eu à construire.

Pousser le travail vers la périphérie avec les CDN et l’informatique de périphérie

Un CDN a commencé comme un moyen de mettre en cache des fichiers statiques près des utilisateurs, et il le fait encore superbement : images, scripts, vidéo, et téléchargements servis depuis un emplacement de périphérie à quelques millisecondes plutôt qu’une origine distante. Les CDN modernes vont plus loin. Ils mettent en cache le contenu dynamique et personnalisé avec des clés à grain fin, terminent TLS à la périphérie, absorbent les pics de trafic et les attaques par déni de service distribué, et exécutent de plus en plus votre code. L’informatique de périphérie exécute la logique aux emplacements de périphérie eux-mêmes, pour que vous puissiez personnaliser une réponse, vérifier l’autorisation, ou assembler un fragment de page sans un aller-retour vers une région centrale.

Appuyez-vous sur cela pour les lectures qui dominent la plupart des systèmes. Placez les actifs statiques derrière le CDN avec des URL versionnées de longue durée, mettez en cache les réponses API à la périphérie là où la fraîcheur le permet (avec des clés soigneuses pour que la personnalisation ne fuit pas), et utilisez le calcul de périphérie pour la logique légère et sensible à la latence proche des utilisateurs. Le compromis est la portée contre le contrôle : la périphérie est rapide et proche mais loin de vos données et plus difficile à déboguer, donc gardez tout ce qui exige une cohérence forte ou un état faisant autorité frais dans l’origine et laissez la périphérie gérer le vaste trafic de lecture mettable en cache.

Traiter le cache comme une surface d’attaque

Un cache sert la même réponse stockée à de nombreux utilisateurs, ce qui en fait une cible. L’empoisonnement de cache est une attaque où une requête est fabriquée pour que le cache stocke une réponse nuisible ou contrôlée par l’attaquant puis la serve à tous ceux qui suivent. Elle exploite généralement une entrée non incluse dans la clé : un en-tête que l’application reflète dans la réponse mais que le cache ignore lors de la construction de la clé. L’attaque connexe de tromperie de cache web trompe un cache pour qu’il stocke la réponse privée d’une victime sous une URL publique. Les deux sont des échecs de mise en clé et de confiance dans les entrées, couverts plus largement au chapitre 4.2.

Défendez-vous délibérément. Incluez dans la clé de cache chaque entrée qui peut changer la réponse, et refusez de refléter des en-têtes non inclus dans la clé dans les corps mis en cache. Ne laissez jamais un cache partagé stocker des réponses authentifiées et par utilisateur sous une clé partagée. Normalisez et validez les chemins de requête et paramètres avant la mise en cache. Fixez Vary correctement pour que les caches partitionnent les réponses par les en-têtes qui comptent réellement, comme l’encodage de contenu ou la langue. Parce qu’une seule entrée empoisonnée nuit à chaque utilisateur en aval, traitez la configuration de cache comme du code sensible à la sécurité et relisez-la en tant que tel.

Rendre le comportement de cache observable

Vous ne pouvez pas gérer un cache que vous ne pouvez pas voir. La métrique phare est le taux de succès : la fraction de requêtes servies depuis le cache plutôt que l’origine. Un taux de succès qui chute discrètement de 95 à 70 pour cent peut multiplier la charge d’origine par plusieurs et précéder une panne, et vous ne l’attraperez tôt que si vous le surveillez. Instrumentez chaque couche séparément, puisqu’un taux de succès CDN sain peut cacher un taux de succès de cache d’application qui s’effondre en dessous. C’est le visage spécifique à la mise en cache des pratiques d’observabilité du chapitre 9.2.

Suivez plus que les succès : le taux d’éviction et la pression mémoire pour attraper le sous-dimensionnement, la latence à chaque couche pour confirmer que le cache est réellement plus rapide, le taux de requête d’origine pour voir combien de charge le cache absorbe, et la péremption (à quel point les entrées servies sont vieilles) pour confirmer que vous honorez vos promesses de fraîcheur. Alertez sur les ratios qui prédisent des ennuis, spécialement un taux de succès en baisse ou un taux d’éviction en hausse, pour apprendre qu’un cache se dégrade depuis un tableau de bord plutôt que depuis des utilisateurs. Un cache observé est un actif que vous pouvez régler ; un non observé est une dépendance cachée qui attend de vous surprendre.

Compromis : avantages et inconvénients

La mise en cache achète de la vitesse et de l’échelle avec la monnaie de la fraîcheur et de la complexité. Chaque cache est un pari que périmé-mais-rapide bat frais-mais-lent pour ces données particulières, et l’art est de faire ce pari consciemment plutôt que par défaut. Le tableau ci-dessous résume les principaux choix.

ChoixAvantagesInconvénients
Cache-asideSimple ; ne met en cache que ce qui est luPremière lecture toujours manquée ; risque de brève péremption après écritures
Write-throughCache toujours actuel à l’écritureÉcritures plus lentes ; met en cache des données qui ne seront peut-être jamais lues
Write-backÉcritures très rapides ; absorbe les rafalesRisque de perte de données si le cache échoue avant le vidage
Write-aroundÉvite le renouvellement de cache par des données lourdes en écritureManqué garanti à la première lecture
TTL courtPéremption bornée et petiteTaux de succès plus bas ; plus de charge d’origine
TTL long / clés versionnéesTaux de succès élevé ; faible charge d’originePéremption sauf si invalidé ; exige des clés disciplinées
CDN et périphérieFaible latence mondiale ; absorbe les picsLoin des données ; plus difficile à déboguer et invalider
Éviction LRUS’adapte à la popularité changeantePeut évincer un ensemble chaud stable sous charge de balayage lourd
Éviction LFUProtège un ensemble chaud stableLent à s’adapter ; s’accroche aux entrées autrefois chaudes

La tension récurrente est la cohérence contre la performance. Un cache avec un long TTL et un taux de succès élevé est rapide et bon marché et peut servir des données périmées ; un cache avec un TTL court et une invalidation agressive est frais et correct et travaille davantage l’origine. Il n’y a pas de bonne réponse universelle, seulement une bonne réponse par pièce de données, fixée par sa vraie tolérance à la péremption. La seconde tension est la simplicité contre la portée : un cache d’application est proche de vos données et facile à raisonner, tandis que la périphérie est loin, rapide, et plus difficile à invalider. Résolvez les deux en classant vos données par besoin de fraîcheur et volume de lecture, puis en plaçant et configurant chaque classe délibérément.

Questions à discuter avec votre équipe

  1. Quelle est la vraie tolérance de péremption de chaque type de donnée que nous mettons en cache, et avons-nous fixé les TTL et l’invalidation à partir de cette tolérance plutôt que de l’habitude ? La plupart des équipes mettent en cache avec un TTL que quelqu’un a choisi une fois et n’a jamais revisité, donc certaines données sont servies plus périmées que ce que l’affaire peut accepter tandis que d’autres expirent si agressivement que le cache aide à peine. Apportez vos dix ressources mises en cache les plus importantes et, pour chacune, demandez aux gens qui possèdent ces données à quel point elles peuvent en sécurité être périmées : secondes, minutes, heures, ou jamais une fois publiées. Vous trouverez généralement que les réponses varient largement et que vos TTL actuels ne leur correspondent pas. Le résultat que vous voulez est une courte classification de fraîcheur, chaque classe assortie à une approche (TTL court, TTL long plus purge, ou clés immuables versionnées), pour que les décisions de mise en cache suivent de la sémantique des données au lieu de la supposition.

  2. Si notre entrée de cache la plus populaire expirait en ce moment sous trafic de pointe, que se passerait-il pour l’origine ? Cette question expose si vous avez une vraie protection contre les ruées ou seulement de l’espoir. De nombreux systèmes fonctionnent bien jusqu’à ce qu’une clé chaude expire pendant un pic de trafic et que chaque requête se précipite sur la base de données à la fois, transformant le cache d’un bouclier en un déclencheur. Parcourez le chemin concrètement pour votre point de terminaison le plus fréquenté : y a-t-il coalescence de requêtes pour qu’un seul manqué atteigne l’origine, y a-t-il de la gigue pour que les entrées n’expirent pas en lockstep, y a-t-il une politique stale-while-revalidate pour que les utilisateurs n’attendent jamais un remplissage ? Apportez une preuve de test de charge, pas de l’intuition, parce qu’une défense contre les ruées qui tient à dix utilisateurs peut encore s’effondrer à dix mille. Si vous ne pouvez pas répondre avec confiance, votre prochain investissement de résilience vient de se trouver.

  3. Sommes-nous certains qu’aucun cache partagé ne stocke jamais les données privées d’un utilisateur sous une clé qu’un autre utilisateur peut atteindre ? C’est l’erreur de mise en cache qui devient un incident de sécurité et une une. Cela arrive quand une réponse personnalisée ou authentifiée est mise en cache sous une clé qui omet l’identité utilisateur, ou quand un en-tête Cache-Control censé garder une réponse privée manque, donc un proxy ou CDN partagé la stocke et la sert à la personne suivante. Auditez quelles réponses sont mettables en cache aux couches partagées, confirmez que chaque réponse par utilisateur est marquée private ou no-store, et confirmez que chaque clé de cache inclut chaque entrée qui change la réponse. Traitez cela comme une relecture de sécurité, parce que le rayon d’impact est chaque utilisateur en aval, et rattachez-le aux pratiques du chapitre 4.2.

  4. Quel motif d’écriture chacun de nos caches utilise-t-il réellement, et quelqu’un l’a-t-il choisi délibérément ? Cache-aside, write-through, write-back, et write-around font des promesses opposées sur la fraîcheur et sur ce que vous perdez quand le cache échoue, pourtant dans la plupart des bases de code le motif est quel que soit ce que le premier auteur a copié. Pour une grande équipe, cela compte parce qu’un service supposant la fraîcheur cache-aside pendant qu’un autre fait discrètement du write-back peut produire des données qui semblent corrompues mais sont seulement périmées, et l’ingénieur d’astreinte perd des heures à chasser un fantôme. Apportez un inventaire par cache : le motif d’écriture, la fraîcheur qu’il garantit, et ce qui arrive aux écritures non vidées si le processus meurt. Là où un cache utilise le write-back, apportez l’histoire de durabilité qui le soutient. Dans les systèmes d’entreprise et gouvernementaux gérant des données financières ou de registres, un cache write-back sans garantie de soutien est une constatation d’audit qui attend de se produire, donc la discussion devrait se terminer avec le motif de chaque cache nommé, justifié, et consigné.

  5. Quand nous déployons ou changeons des données, chaque couche de cache pertinente invalide-t-elle correctement, ou comptons-nous sur quelqu’un se souvenant de purger ? L’invalidation est la partie difficile, et le mode d’échec est silencieux : une valeur corrigée qui reste fausse pendant des heures parce qu’une couche de la hiérarchie, un CDN, un proxy inverse, ou un cache d’application, n’a jamais reçu le message. Une grande organisation multiplie ce risque, parce qu’un seul changement logique peut avoir besoin de se propager à travers de nombreux caches dans de nombreuses régions possédées par différentes équipes. Apportez une trace concrète d’un changement de données récent et suivez-la à travers chaque couche de cache, demandant à chacune : qu’est-ce qui a déclenché l’invalidation ici, et combien de temps cela a-t-il pris ? Favorisez les conceptions qui transforment l’invalidation en nommage (clés versionnées) ou en événements (un changement publie une purge) plutôt que des manuels manuels. Dans les systèmes du secteur public où un mauvais chiffre publié, un taux d’imposition ou un montant de prestation, peut porter un poids légal, une lacune d’invalidation n’est pas un désagrément mais une exposition de conformité, donc le résultat devrait être un chemin d’invalidation cartographié pour chaque classe de données mise en cache.

  6. Traitons-nous la mise en cache comme de l’infrastructure de plateforme partagée, ou chaque équipe réinvente-t-elle les clés, l’invalidation, et la protection contre les ruées de son côté ? La mise en cache bien faite est un petit ensemble de problèmes difficiles résolus une fois : clés normalisées, invalidation pilotée par événements, coalescence de requêtes, sémantique HTTP correcte, et observabilité par couche. Quand chaque équipe improvise cela, une grande organisation paie répétitivement pour les mêmes erreurs, et un bug d’empoisonnement ou une fuite de données privées corrigée dans un service persiste silencieusement dans dix autres. Apportez une carte honnête de qui possède les conventions de mise en cache aujourd’hui et combien de code de mise en cache dupliqué existe à travers les services. La considération concurrente est l’autonomie : les équipes résistent à une bibliothèque partagée mandatée, donc pesez une valeur par défaut de route pavée facile à adopter contre une norme dure imposée. Pour un groupe de plateforme d’entreprise ou gouvernemental, une capacité de mise en cache partagée et bien testée est aussi le moyen le moins cher de faire tenir les exigences de sécurité et d’audit uniformément, donc la discussion devrait décider ce qui devient une infrastructure partagée et qui la finance.

Regard sectoriel

Jeune pousse. La mise en cache est votre chemin le moins cher pour survivre à un pic de trafic que vous ne pouvez pas encore vous permettre d’échelonner, donc dépensez le peu de temps que vous avez sur quelques placements à haut levier : un CDN avec des URL versionnées pour les actifs statiques, et une seule couche cache-aside avec des TTL courts et de la gigue devant votre requête la plus chaude. Appuyez-vous sur des services CDN et de cache gérés plutôt que d’en exploiter les vôtres, et ajoutez la coalescence de requêtes tôt, parce qu’une ruée du jour de lancement contre une petite base de données est l’échec le plus susceptible de gâcher une bonne journée. Sautez les schémas d’invalidation élaborés jusqu’à ce que vous ayez des données vous disant qu’ils comptent.

Petite entreprise. Sans spécialiste de mise en cache et avec un budget serré, préférez acheter la mise en cache que vous obtenez gratuitement à l’intérieur des outils que vous exploitez déjà : un CDN groupé avec votre hébergement, des en-têtes HTTP Cache-Control sur les réponses de votre framework web, et le cache de requête intégré de votre base de données. Le choix acheter-contre-construire favorise presque toujours acheter ici, puisqu’un cache mal mis en clé qui fuit les données d’un client vers un autre coûte bien plus que le service géré que vous avez évité. Obtenez les deux gains bon marché correctement, des en-têtes HTTP corrects et ne jamais mettre en cache des pages authentifiées aux couches partagées, et laissez tranquilles les motifs exotiques.

Grande entreprise. À l’échelle à travers de nombreuses équipes, le risque se déplace d’un seul cache vers l’incohérence entre eux : des schémas de clé divergents, une invalidation inégale, et des fuites de données privées qui apparaissent dans un service et pas un autre. Fournissez la mise en cache comme infrastructure de plateforme partagée avec des valeurs par défaut de route pavée pour les clés, l’invalidation, la protection contre les ruées, et l’observabilité par couche, pour que le taux de succès, l’éviction, et la péremption soient visibles en un seul endroit et gouvernés uniformément. Rendez la configuration de cache relisable comme du code sensible à la sécurité, et traitez l’invalidation à travers les régions comme un problème de conception de premier ordre plutôt qu’un manuel par équipe.

Gouvernement. Les contraintes d’approvisionnement et de transparence façonnent ce que vous pouvez mettre en cache et comment vous prouvez que c’est sûr. Mettez en cache agressivement le contenu public, les conseils, les formulaires, et les tables de taux derrière un CDN avec de longs TTL, pour qu’un pic d’échéance de dépôt soit absorbé loin de l’origine, et documentez cette configuration pour audit. Les pages authentifiées montrant les propres dossiers d’un citoyen ne doivent jamais toucher un cache partagé, et cette règle devrait être vérifiable, pas simplement affirmée. Là où un CDN ou service de mise en cache est approvisionné auprès d’un fournisseur, exigez que le contrat expose les contrôles dont vous avez besoin (mise en clé, purge, et journalisation) et évitez l’enfermement qui piégerait les données publiques derrière des formats de cache propriétaires.

Exemples

Jeune pousse. Une petite application grand public fait fonctionner son catalogue de produits à travers une couche cache-aside soutenue par un magasin en mémoire, avec un TTL de 60 secondes et de la gigue pour que les entrées n’expirent pas ensemble. Les actifs statiques vont vers un CDN avec des noms de fichier versionnés et un max-age d’un an, donc un déploiement qui change une feuille de style sert une nouvelle URL et n’a jamais besoin de purge. Quand un lancement sur un podcast populaire envoie un pic de trafic, la coalescence single-flight signifie que les milliers de manqués simultanés de page d’accueil causent une lecture de base de données, pas des milliers. Les fondateurs dépensent presque rien en mise en cache et pourtant gèrent un pic qui aurait fait fondre leur petite base de données, parce qu’ils ont placé quelques caches bien choisis délibérément.

Grande entreprise. Un détaillant mondial sert des millions d’acheteurs à travers un cache stratifié : un CDN pour les images et réponses API mettables en cache, un cache de proxy inverse partagé dans chaque région, et un cache d’application pour les fragments de tarification et d’inventaire calculés. Les clés de cache sont normalisées et incluent la devise, la langue, et la classe d’appareil, donc la personnalisation ne fuit jamais et les taux de succès restent élevés. Les données de produit utilisent des TTL courts avec une invalidation pilotée par événements, donc un changement de prix publie sur un bus de messages qui purge les clés affectées à travers les régions en quelques secondes. Chaque couche rapporte le taux de succès, le taux d’éviction, et la péremption à la plateforme d’observabilité du chapitre 9.2, et une alerte sur un taux de succès en baisse a une fois attrapé un cache sous-dimensionné avant qu’il ne devienne une panne de paiement.

Gouvernement. Une administration fiscale nationale exploite un portail de dépôt qui est calme la majeure partie de l’année et submergé près de l’échéance. L’équipe met en cache agressivement là où c’est sûr et jamais là où ce ne l’est pas. Le contenu public (pages de conseils, formulaires, tables de taux) est servi depuis un CDN avec de longs TTL et des URL versionnées, absorbant le pic de lecture du jour d’échéance loin de l’origine. Les pages authentifiées montrant le propre dépôt d’un citoyen sont marquées no-store et ne touchent jamais un cache partagé, donc aucun contribuable n’est jamais servi les données d’un autre. La configuration de cache est relue comme du code sensible à la sécurité contre les pratiques du chapitre 4.2, et la protection contre les ruées est testée par charge à l’échelle de l’échéance des mois à l’avance, donc le portail qui pliait autrefois le jour le plus fréquenté de l’année tient maintenant.

Argumentaire économique : motivations, retour sur investissement et coût total de possession

Le retour sur la mise en cache est inhabituellement direct et facile à quantifier. Un cache qui augmente le taux de succès de 80 à 95 pour cent réduit le trafic d’origine de trois quarts, ce qui peut signifier reporter une mise à niveau de base de données, exploiter moins de serveurs d’application, ou survivre à un pic de trafic qui aurait autrement exigé une mise à l’échelle d’urgence. Les améliorations de latence se convertissent en revenu dans le commerce et en satisfaction et taux d’achèvement dans les services publics, où la recherche a longtemps lié des pages plus rapides à une conversion plus élevée et un abandon plus bas. Les coûts de sortie et de calcul baissent parce qu’une requête servie depuis la périphérie ne paie jamais pour la bande passante ou le traitement d’origine. Pour les systèmes lourds en lecture, ce que sont la plupart des systèmes, la mise en cache est souvent la performance la moins chère que vous puissiez acheter.

Pesez honnêtement le coût total de possession. Les coûts directs sont modestes : l’infrastructure CDN et de cache est bon marché relativement à la capacité d’origine qu’elle économise. Le vrai coût est la discipline d’ingénierie, parce qu’un cache qui est faux est pire que pas de cache. Les bugs de péremption, les erreurs d’invalidation, et les vulnérabilités d’empoisonnement de cache portent tous un vrai coût, et ils grandissent quand la mise en cache est improvisée par équipe au lieu d’être fournie comme une capacité partagée et bien testée. L’argumentaire économique le plus fort finance une petite quantité d’infrastructure et de convention de mise en cache partagée (clés standard, invalidation, protection contre les ruées, et observabilité) pour que chaque équipe obtienne le bénéfice sans répéter les erreurs. Cadré pour la direction, la mise en cache se rattache aux métriques qu’elle suit déjà : coût d’infrastructure, latence de page, taux de conversion et d’achèvement, et fréquence d’incident pendant les événements de pointe.

Anti-patterns et pièges

  • Mettre en cache sans invalidation : fixer un long TTL sans moyen de purger, donc une valeur corrigée reste fausse pendant des heures.
  • Clés trop grossières : mettre en cache des réponses personnalisées sous une clé partagée, fuyant les données d’un utilisateur vers un autre.
  • Clés trop fines : inclure des entrées volatiles dans la clé pour qu’aucune deux requêtes ne correspondent jamais et que le taux de succès s’effondre.
  • Pas de protection contre les ruées : une clé chaude expire sous charge et chaque requête se précipite sur l’origine à la fois.
  • Expiration synchronisée : un lot d’entrées écrites ensemble expirent toutes au même instant sans gigue, causant un troupeau tonitruant périodique.
  • Mettre en cache des données privées aux couches partagées : Cache-Control: private ou no-store manquant, donc un proxy ou CDN stocke des réponses authentifiées.
  • Ignorer les entrées non incluses dans la clé : refléter un en-tête dans le corps de réponse mais l’omettre de la clé, ouvrant la porte à l’empoisonnement de cache.
  • Cache sous-dimensionné : un cache trop petit pour contenir l’ensemble de travail thrashe et évince des entrées juste avant qu’elles ne soient nécessaires.
  • Cache non mesuré : aucune métrique de taux de succès ou d’éviction, donc un cache en dégradation est invisible jusqu’à ce qu’il devienne une panne.
  • Write-back sans durabilité : des écritures rapides qui disparaissent quand le cache meurt avant le vidage, sans garantie de soutien.

Modèle de maturité

  • Niveau 1 (Initier) : La mise en cache est ad hoc et par développeur, ajoutée réactivement quand quelque chose semble lent. Les TTL sont devinés, les clés sont incohérentes, l’invalidation est manuelle ou absente, et les données périmées et les bugs mystérieux sont courants. Personne ne suit le taux de succès, et un pic de trafic qu’un cache aurait dû absorber cause plutôt une panne.
  • Niveau 2 (Développer) : Les équipes mettent en cache aux endroits évidents et utilisent un CDN pour les actifs statiques. Des TTL de base et le cache-aside apparaissent, mais les conventions varient de service à service, l’invalidation est incohérente, la protection contre les ruées manque, les règles de mise en cache privée-contre-partagée sont informelles, et l’observabilité se limite à des vérifications ponctuelles occasionnelles.
  • Niveau 3 (Standardiser) : Une stratégie de mise en cache est documentée et imposée à travers l’organisation. Les clés de cache sont normalisées, les TTL suivent une classification de fraîcheur partagée, l’invalidation est pilotée par événements là où cela compte, la protection contre les ruées et la sémantique HTTP correcte sont standard, les données privées ne sont jamais mises en cache aux couches partagées, et chaque couche rapporte le taux de succès et l’éviction à un pipeline d’observabilité commun.
  • Niveau 4 (Gérer) : La mise en cache est mesurée et contrôlée par rapport à des références. Chaque couche a des taux de succès cibles, des budgets de péremption, et des seuils d’éviction, et les tableaux de bord alertent quand un taux de succès baisse ou qu’un taux d’éviction dépasse sa référence. Les défenses contre les ruées sont testées par charge à l’échelle de pointe, la réduction de charge d’origine est quantifiée par cache, les TTL et politiques d’éviction sont réglés à partir de motifs d’accès mesurés, et la configuration de cache est relue comme du code sensible à la sécurité avant la livraison.
  • Niveau 5 (Orchestrer) : La mise en cache est continuellement améliorée et intégrée à travers l’organisation. Le placement, les clés, et les TTL s’adaptent au trafic changeant, le calcul de périphérie est utilisé là où il mérite sa place, la planification de capacité et les modèles de coût s’appuient sur les métriques de cache, et les leçons des incidents d’une équipe alimentent les conventions partagées. L’organisation traite la mise en cache comme une capacité conçue, mesurée, et adaptative plutôt qu’une collection de bricolages locaux.

Pistes de réflexion

  1. Quel cache unique dans votre système, s’il devenait froid en ce moment, mettrait le plus en danger votre origine, et qu’est-ce qui le protège ?
  2. Pour chaque couche de votre hiérarchie de cache, pouvez-vous nommer son taux de succès actuel de mémoire, et sinon, que cela vous dit-il ?
  3. Où avez-vous transformé un problème d’invalidation en problème de nommage avec des clés versionnées, et où pourriez-vous encore le faire ?
  4. Lequel de vos chemins d’écriture utilise le cache-aside, le write-through, le write-back, ou le write-around, et chacun a-t-il été choisi délibérément ?
  5. Si un attaquant contrôlait un en-tête de requête, pourrait-il empoisonner toute réponse mise en cache que vos utilisateurs partagent ?
  6. Comment sauriez-vous, en quelques minutes, que votre taux de succès avait discrètement chuté de vingt points ?

Points clés à retenir

  • La mise en cache réduit la latence, la charge, et le coût, et la hiérarchie de cache (client, CDN et périphérie, proxy inverse, application, base de données) vous permet de répondre aussi loin en amont et en dehors que vous le pouvez en sécurité.
  • L’invalidation est la partie difficile ; concevez délibérément les clés de cache et les TTL, et transformez les problèmes d’invalidation en problèmes de nommage avec des clés versionnées partout où vous le pouvez.
  • Protégez le cache sous charge avec la coalescence de requêtes, la gigue, et le stale-while-revalidate, parce qu’un cache est le plus nécessaire exactement quand une ruée pourrait le casser.
  • Choisissez les motifs d’écriture et les politiques d’éviction par charge de travail, et utilisez la sémantique de mise en cache HTTP (Cache-Control, ETags, validation) explicitement plutôt que de laisser les caches deviner.
  • Traitez le contenu mis en cache comme une surface d’attaque et mesurez le taux de succès, l’éviction, et la péremption, parce qu’un cache non observé ou mal mis en clé est un passif caché, pas un actif.

Références et lectures complémentaires

  • Martin Kleppmann, Designing Data-Intensive Applications
  • Andrew S. Tanenbaum et Herbert Bos, Modern Operating Systems
  • John L. Hennessy et David A. Patterson, Computer Architecture: A Quantitative Approach
  • Roy T. Fielding et Julian Reschke, « Hypertext Transfer Protocol (HTTP/1.1): Caching », RFC 7234, IETF
  • Mark Nottingham, « Caching Tutorial for Web Authors and Webmasters »
  • James Kettle, « Practical Web Cache Poisoning », PortSwigger Research
  • Betsy Beyer, Chris Jones, Jennifer Petoff, et Niall Richard Murphy (dir.), Site Reliability Engineering: How Google Runs Production Systems
  • Michael T. Nygard, Release It! Design and Deploy Production-Ready Software