Какие триггерные события могут вызывать обновление кэша?
Что проверяют
Понимаете ли вы, что кэш устаревает не сам по себе, а по конкретным событиям, и умеете ли выбрать стратегию инвалидации под задачу. Следом обычно спрашивают, что будет, если в момент истечения ключа на него придёт тысяча запросов.
Как отвечать
Разложите триггеры на группы и для каждой назовите механизм в Python-стеке:
- истечение TTL: самый простой вариант.
SETEXилиEXPIREв Redis,timeoutвcache.set()у Django,TTLCacheизcachetools. Уfunctools.lru_cacheTTL нет вообще, толькоcache_clear(); - запись в источник: при изменении данных код удаляет ключ. Классический cache-aside: сначала пишем в базу, потом удаляем ключ, следующее чтение наполнит кэш заново. В Django для этого вешают обработчики на
post_saveиpost_delete; - события извне: сообщение в очереди,
LISTEN/NOTIFYв PostgreSQL, CDC-поток через Debezium. Нужны, когда базу меняет не только ваш сервис; - вытеснение по памяти: Redis при достижении
maxmemoryудаляет ключи по политике вродеallkeys-lru. По умолчанию стоитnoeviction, и тогда запись просто начинает падать с ошибкой; - деплой и смена формата: новая версия кода меняет структуру объекта. Помогает версия в ключе (
user:v3:42), старые записи тогда умирают по TTL; - ручной сброс: команда или кнопка в админке на случай инцидента.
Хороший ответ сочетает механизмы: явная инвалидация ради точности и TTL как страховка на случай, если событие потерялось.
Где спотыкаются
Не знают, что QuerySet.update() и bulk_create() не отправляют post_save. Инвалидация на сигналах в таком коде тихо перестаёт работать.
Не думают о лавине (cache stampede): популярный ключ истёк, и все воркеры разом идут в базу. Помогают блокировка на пересчёт, случайный разброс TTL или обновление заранее.
Используют lru_cache в приложении под gunicorn и ждут общей инвалидации. У каждого процесса свой кэш, сбросить его из соседнего воркера нельзя.