Cache Stampede — is een lawineachtige toename van verzoeken naar de gegevensbron die optreedt wanneer meerdere clients of threads gelijktijdig een cachemisser ervaren. In mobiele apps treedt Cache Stampede op wanneer een populair cache-item verloopt en honderden apparaten tegelijkertijd proberen gegevens te herladen, wat overbelasting van de server veroorzaakt. Volgens onderzoek van Medium Engineering (2024) vermindert correct geconfigureerde bescherming tegen Cache Stampede de piekbelasting van de server met tot 95%.
Belangrijkste punten
Cache Stampede (ook bekend als cache thundering herd) — is een situatie waarin een groot aantal verzoeken tegelijkertijd een cachemisser ontdekken en naar de gegevensbron worden gestuurd. Dit creëert een piekbelasting die kan leiden tot degradatie of volledige uitval van het systeem.
Typisch scenario: een mobiele app cacht een gemeenschappelijke productlijst met TTL = 5 minuten. Om 14:00 verloopt de cache. 500 actieve gebruikers openen tegelijkertijd de catalogus, elke gebruiker ontdekt een lege cache en alle 500 verzoeken gaan naar de server. De database of externe API kan de plotselinge stroom niet aan, de pagina laadt 10-15 seconden, een deel van de verzoeken krijgt een timeout.
Volgens gegevens van Vattani et al. (SOCC, 2015) treedt Cache Stampede op in elke cachelaag met verlopende items, waaronder CDN, Redis, Memcached, de HTTP-cache van de browser en de in-memory cache van de app. Hoe populairder het item — hoe groter de potentiële schade door stampede.
def mutex_get(key, lock_timeout=5):
cached = cache.get(key)
if cached is not None:
return cached
if lock.acquire(key, lock_timeout):
data = source.load(key)
cache.set(key, data)
lock.release(key)
return data
sleep(0.01)
return mutex_get(key, lock_timeout)
Dit voorbeeld toont bescherming via mutex: slechts de eerste thread werkt de cache bij, de rest wacht op het gereed resultaat. Het vergrendelingsmechanisme — de eenvoudigste maar effectieve manier om stampede te voorkomen bij matige belastingen.
Massaal verlopen van TTL — de belangrijkste oorzaak van Cache Stampede. Wanneer een populair cache-item dezelfde TTL heeft voor alle clients, ontdekken ze allemaal tegelijk de afwezigheid ervan. Dit is typisch voor gemeenschappelijke gegevens: wisselkoersen, lijst van landen, basisconfiguratie van de app.
Instorting bij serverherstart — als Redis of Memcached wordt gereset, is de volledige cache leeg. Bij de eerste piek in verkeer gaan alle verzoeken naar de database. Volgens Amazon AWS Architecture Blog (2024) wordt aanbevolen om de cache na herstart op te warmen: geleidelijk populaire items laden om een plotselinge belastingpiek te voorkomen.
Fout in de invalidatielogica — wanneer de ontwikkelaar bij wijziging van één item de cache voor alle gebruikers wist. Bijvoorbeeld in een nieuwsapp wordt bij publicatie van één artikel de hele nieuwslijst geïnvalideerd. Honderden gebruikers ontdekken tegelijk een lege cache — en de server valt uit. Puntsgewijze invalidatie (alleen van het gewijzigde item) verhelpt dit probleem.
Koude start van de app — op mobiele apparaten bestaat de cache alleen in het geheugen van het proces. Na herstart van de app is de cache leeg en gaan alle verzoeken naar de server. Oplossing: cache opslaan op schijf tussen sessies (Room, SQLite) en vooraf laden van belangrijke gegevens bij de start.
Idee van de methode — bij cachemisser neemt de eerste thread een vergrendeling (lock) en begint met het laden van gegevens uit de bron. De andere threads wachten op het vrijkomen van de vergrendeling en lezen de bijgewerkte cache. De vergrendeling kan worden geïmplementeerd via Redis SETNX, ZooKeeper of zelfs een in-memory mutex.
De belangrijkste parameter — lock_timeout. Als deze te kort is, kan de eerste thread de cache niet op tijd bijwerken en zal de tweede thread ook proberen gegevens te laden, waardoor een mini-stampede ontstaat. Als deze te lang is — wachten clients langer dan nodig. Aanbevolen waarde: 2-5 seconden voor typische databaseverzoeken.
Probleem van één thread — bij falen van de eerste thread (uitzondering, timeout) blijft de vergrendeling ingenomen en wachten alle andere threads tot het verlopen van lock_timeout. Oplossing: verwijder de vergrendeling in het finally-blok. Volgens Redis Best Practices (2025) wordt aanbevolen Lua-scripts te gebruiken voor het atomair instellen en vrijgeven van vergrendelingen.
-- Atomic lock acquisition script for Redis
if redis.call("SET", KEYS[1], "locked", "NX", "EX", ARGV[1]) then
return true
else
return false
end
Dit Lua-script controleert en stelt atomair een vergrendeling in met TTL. Atomiciteit garandeert dat twee gelijktijdige verzoeken geen vergrendeling kunnen krijgen — slechts één, wat Cache Stampede volledig voorkomt.
Probabilistic Early Expiration (PEE) — een elegante methode zonder vergrendelingen. Het idee is dat elke client met een bepaalde kans de cache herberekent vóór de daadwerkelijke vervaldatum. Hoe dichter het item bij vervaldatum is — hoe groter de kans. Dit verdeelt herladingen natuurlijk in de tijd en de piekbelasting wordt afgevlakt.
Het XFetch-algoritme — voorgesteld door Vattani, Chierichetti en Panconesi (2015). De kansformule: p = max(0, 1 - (beta * age / ttl)), waarbij beta — de ongelijkmatigheidsparameter is (meestal 1.0). Bij age = 0 → p = 1 (gegarandeerde bijwerking op leeftijd nul, wat onjuist is). Daarom wordt een modificatie gebruikt: p = max(0, (ttl - age) / (ttl * beta)).
Volgens gegevens van Etsy Engineering (2024) verminderde de implementatie van XFetch in Memcached het aantal stampede-gebeurtenissen van 12 per dag naar 0, en de piekbelasting van de database daalde met 78%. Gerandomiseerde bijwerking vereist geen vergrendelingen en synchronisatie, wat XFetch ideaal maakt voor microservicesarchitectuur.
Voor mobiele clients kan PEE aan de app-kant worden toegepast: clients starten willekeurig het bijwerken van de cache vóór de officiële vervaldatum. Bijvoorbeeld bij age > 80% TTL werkt elk verzoek met 20% kans de gegevens bij. Gedistribueerde mobiele apparaten creëren natuurlijke ruis die de kans op een synchrone impact verder vermindert.
Idee van de aanpak — de cache bijwerken voordat deze verloopt, waardoor misser volledig wordt geëlimineerd. Een achtergrondproces (cron, scheduler in Kubernetes) controleert periodiek populaire sleutels en overschrijft ze met een nieuwe TTL. Clients vinden de cache altijd geldig — stampede is per definitie onmogelijk.
Time-To-Refresh (TTR) — parameter die bepaalt hoe lang vóór vervaldatum met bijwerken moet worden begonnen. Bijvoorbeeld TTL = 300 seconden, TTR = 60 seconden: op 240 seconden overschrijft het achtergrondproces de gegevens. Dit garandeert dat clients nooit een lege cache zien.
Nadeel van preventieve bijwerking — constante belasting van de gegevensbron, zelfs als niemand het item opvraagt. Voor zelden gebruikte gegevens is dit inefficiënt. Oplossing: de frequentie van verzoeken naar een sleutel (access frequency) volgen en alleen populaire items bijwerken. Adaptieve TTR — een geavanceerde techniek waarbij het bijwerkingsinterval dynamisch wordt berekend op basis van de verzoekgeschiedenis.
Mutex-vergrendeling — geschikt voor systemen met matige belasting (tot 10 000 rps per sleutel). Eenvoudig te implementeren en garandeert dat de bron slechts één bijwerkingsverzoek ontvangt. Nadeel — vergrendelingen veroorzaken vertraging voor wachtende threads.
Probabilistic Early Expiration / XFetch — optimaal voor hoge belastingen en gedistribueerde systemen. Vereist geen vergrendelingen, schaalt horizontaal, piekbelasting wordt natuurlijk afgevlakt. Aanbevolen voor Redis-clusters en Memcached met duizenden clients.
Preventieve bijwerking — ideaal voor kritieke gegevens met voorspelbaar toegangspatroon (configuraties, naslagwerken, basismetadata). Vereist extra infrastructuur voor het achtergrondproces.
Schijfcache op de client — voor mobiele apps het belangrijkste beschermingsniveau. Zelfs als de servercache is geïnvalideerd, kan de mobiele client gegevens van schijf tonen terwijl een nieuw verzoek wordt uitgevoerd. Room + Stale-While-Revalidate — het aanbevolen patroon van Google (2025), dat Cache Stampede aan de clientzijde volledig elimineert.
Veelgestelde vragen
Cache Stampede — het resultaat van normaal gedrag van legitieme clients die tegelijkertijd een lege cache ontdekken. DDoS — een opzettelijke aanval. Voor Stampede zijn beschermende algoritmen voldoende; voor DDoS is extra infrastructuur voor verkeersfiltering nodig.
Monitor de RPS-grafieken op de gegevensbron (database, API). Als je regelmatige belastingpieken ziet gesynchroniseerd met het moment van cache-verval — dan is dit Cache Stampede. Voeg de metriek cache miss rate toe voor elke populaire sleutel.
Ja, als meerdere threads in de app een gemeenschappelijke in-memory cache gebruiken. Bijvoorbeeld 10 coroutines vragen tegelijkertijd het gebruikersprofiel op — de eerste blokkeert, de overige 9 kunnen het verzoek dupliceren. De Kotlin-bibliotheek kotlinx.coroutines lost dit op via CoroutineCache of Flow.distinctUntilChanged.
beta = 1.0 — de standaardwaarde die een gelijkmatige verdeling van herberekeningen geeft. Voor agressievere bescherming (kleinere kans op misser) verhoog beta naar 1.5–2.0. Voor het besparen van bronresources — verlaag naar 0.5. Aanbevolen bereik: 0.8–1.2.
Ja, via het Cache-Control: stale-while-revalidate en Cache-Control: stale-if-error mechanisme. CDN retourneert de verouderde versie en werkt op de achtergrond de cache bij, wat het CDN-equivalent is van PEE. Cloudflare en Fastly ondersteunen deze directives sinds 2023.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook