Назад в блог
Инженерия
1 мин чтения
RedisPython

Баг с кешем, после которого я перестал доверять TTL

Цена товара продолжала показываться старой после обновления. Проблемой был кеш, опиравшийся только на TTL; решением стала инвалидация по тегам, которую я использую до сих пор, в том числе на этом сайте.

E-commerce бэкенд, который я строил, кешировал каталог товаров в Redis. Чтение каталога это самый горячий путь в магазине, а данные почти не меняются, поэтому кешировать их лёгкая победа. Я сделал самое очевидное: закешировал ответ, поставил TTL и пошёл дальше.

Это работало ровно до того момента, когда админ поменял цену. Обновление тут же попало в Postgres, а витрина продолжала отдавать старую цену, пока TTL случайно не истёк. Хорошего TTL здесь не существует: укоротите его и потеряете тот hit rate, ради которого всё и кешировали; удлините и устаревшие цены будут висеть дольше. Ни одно число не бывает одновременно свежим и быстрым.

По-настоящему помогла инвалидация по тегам. Каждая закешированная запись помечается тем, от чего она зависит: запись товара несёт тег этого товара, список категории несёт тег категории. Когда что-то меняется, я не гадаю, какие ключи сбросить, а инвалидирую всё под этим тегом. Обновление цены затрагивает ровно те записи, которые показывали эту цену, и ничего больше. Hit rate остаётся высоким, потому что большинство записей никто не трогает, а устаревание падает с «до одного TTL» до «нескольких миллисекунд после записи».

Я доверяю этому паттерну настолько, что на нём работает и этот сайт. Публичные страницы кешируются в Redis и помечаются тегами; публикация или правка статьи инвалидирует тег, и следующий читатель получает новую версию. Правило, к которому я пришёл: TTL это страховка для записей, о которых вы забыли, а не стратегия инвалидации. Если вы знаете, когда меняются ваши данные, а внутри собственного приложения вы почти всегда это знаете, инвалидируйте по изменению, а не по таймеру.

Поделиться:Поделиться в Telegram
Ещё статьи