Nejlevnější způsob, jak cache vyčistit, je nic z ní nemazat
Změní se katalog a milion položek v cache je rázem neplatných. Naivní řešení je všechny najít a smazat. Vercel při popisu své Runtime Cache ukazuje lepší vzorec, který funguje nezávisle na jejich produktu. Invalidace tagu zapíše jedno časové razítko, ať tag sdílí jeden záznam, nebo milion. O tom, co je čerstvé, se rozhoduje až při čtení.
Platnost se počítá, neukládá
Každý záznam nese své tagy a čas zápisu. Vedle cache běží oddělené úložiště tagů, kde je pro každý tag čas poslední invalidace. Čtení je pak porovnání: pokud byl některý tag zneplatněn později, než vznikl záznam, je záznam starý. Nic se nemusí procházet ani mazat.
Cena se tím ale jen přesunula. Invalidace už nezávisí na počtu záznamů, zato každé čtení navíc potřebuje čerstvost tagů. Proto je úložiště tagů ve stejném regionu jako cache a čtení se neptá přes půl světa. Tenhle obchod se vyplatí tam, kde se čte mnohem častěji, než zneplatňuje. U cache je to z definice obvyklý případ.
Pro úplnost vrstev: CDN drží hotové HTTP odpovědi, tahle cache pod ní drží vstupy, ze kterých odpovědi vznikají. Stránka produktu tak může znovu použít data katalogu a odpověď přesto personalizovat pro každého návštěvníka.
Kde se schovává konzistence
Každý region má vlastní cache a při neúspěchu se neptá ostatních regionů. Invalidace okamžitě platí v regionu, který ji přijal, a do ostatních doputuje obvykle během pár set milisekund. V tomhle okně může jiný region vracet stará data. Kdo potřebuje přečíst vlastní zápis, má jistotu jen v regionu, kde invalidoval.
Zajímavější je chování při výpadku. Když je úložiště tagů pomalé, jističe nechají čtení ověřovat čerstvost podle razítek v paměti. Takové čtení může krátce považovat právě zneplatněná data za čerstvá. Vercel tady vědomě volí dostupnost před konzistencí. Je to rozhodnutí, které má mít každý systém s touhle architekturou vyslovené předem, protože úložiště tagů se jinak stává závislostí každého čtení a jeho výpadek by zastavil celou cache.
Z popsaného pravidla plyne ještě jedna past, kterou zdroj neřeší, a nedá se z něj poznat, jak ji služba ošetřuje. Kdo načte data před invalidací a uloží je až po ní, má pozdější čas zápisu. Podle samotného porovnání časů projde starý obsah jako čerstvý. Až budeš podobný vzorec stavět sám, porovnávej čas začátku výpočtu, ne konce zápisu.
Co si z toho odnést
Až budeš navrhovat hromadnou invalidaci, nehledej rychlejší mazání. Zvaž, jestli platnost nemůže být výsledkem porovnání při čtení. Záznamy doplň o tagy a čas, invalidaci zredukuj na jedno razítko a mazání nech na vypršení platnosti. Spočítej si, kolikrát za sekundu se bude číst úložiště tagů, protože to je teď tvoje skutečné úzké hrdlo. A předem rozhodni, co se stane při jeho výpadku. Buď vrátíš možná stará data, nebo nevrátíš žádná. Obě odpovědi jsou legitimní, nerozhodnout se legitimní není.