Была ли инвалидация кэша и по какой стратегии
Что проверяют
Был ли у вас кэш, который мог отдать устаревшие данные, и думали ли вы об этом заранее. Интервьюеру важно, отличаете ли вы вытеснение от инвалидации и что происходит при конкурентной записи. Следом спрашивают про гонку между чтением из базы и удалением ключа и про лавину промахов после истечения TTL.
Как отвечать
Начните с конкретики проекта: что кэшировали, где (Redis через go-redis или в памяти процесса), насколько критична свежесть. Затем стратегия:
- TTL: самый простой вариант, данные устаревают не дольше срока жизни. Добавляйте к TTL случайный разброс, иначе ключи, записанные одновременно, одновременно и истекут;
- cache-aside с удалением при записи: сначала обновляете базу, потом удаляете ключ, следующее чтение загрузит свежее значение. Удалять надёжнее, чем перезаписывать: два параллельных обновления не перетрут кэш в неправильном порядке;
- событийная инвалидация: изменение публикуется в Kafka, NATS или Redis Pub/Sub, и все инстансы сбрасывают локальный кэш;
- версионирование ключей: версия входит в ключ, после изменения данные читаются по новому ключу, а старый доживает до TTL.
От лавины промахов в Go есть готовый инструмент: singleflight из golang.org/x/sync склеивает одновременные запросы одного ключа в одно обращение к базе. Для локального кэша берут ristretto или hashicorp/golang-lru, а самописная map под sync.RWMutex без ограничения размера со временем съест память.
Если инвалидацию делать не приходилось, честно скажите, что хватало TTL, и объясните, при каких требованиях к свежести перешли бы на удаление при записи.
Где спотыкаются
Называют LRU стратегией инвалидации. LRU отвечает за вытеснение при нехватке места и ничего не знает о том, изменились ли данные в базе.
Не видят гонку: запрос прочитал старое значение из базы, запись обновила базу и удалила ключ, а первый запрос положил в кэш старые данные. Её закрывают коротким TTL как страховкой, отложенным повторным удалением или версиями.
Держат локальный кэш в нескольких репликах сервиса и сбрасывают его только на том инстансе, который обработал запись. Остальные поды отдают старое до истечения TTL.