Как реализовать бесконечный цикл обновления кэша
Что проверяют
Умеете ли вы написать фоновую горутину, которую можно остановить, и кэш, который безопасно читать во время обновления. Следом спрашивают, что будет при ошибке источника и как проверить код на гонки.
Как отвечать
- цикл с остановкой:
ticker := time.NewTicker(interval),defer ticker.Stop(), внутриforблокselectс ветками<-ctx.Done()для выхода и<-ticker.Cдля обновления. Контекст приходит изmain, где его отменяют поSIGTERMчерезsignal.NotifyContext; - безопасное чтение: данные целиком собираются в новый снапшот и подменяются одной операцией. Удобно через
atomic.Pointer[Snapshot]: читатели зовутLoad()без блокировок, обновление делаетStore(). Вариант сsync.RWMutexтоже рабочий, если структура меняется по частям; - ошибки: у каждого обновления свой
context.WithTimeout, при сбое старые данные остаются, пишутся лог и метрика возраста кэша. Повторы идут с экспоненциальной задержкой и джиттером, чтобы десяток инстансов не бил в источник одновременно; - старт и остановка: первая загрузка выполняется синхронно до того, как сервис начнёт отвечать, иначе первые запросы увидят пустой кэш. Завершения горутины ждут через
sync.WaitGroupилиerrgroup.
Добавьте поведение тикера: если обновление длится дольше интервала, лишние тики отбрасываются, а не копятся, поэтому очереди из обновлений не возникает. Если нужна пауза именно между концом одного обновления и началом следующего, берут time.NewTimer и вызывают Reset после работы. Где спотыкаются
Пишут for { time.Sleep(interval); update() } в горутине без выхода. Остановить её нельзя, в тестах и при плавном завершении она утекает, а первое обновление случается только через целый интервал.
Меняют поля кэша прямо из фоновой горутины, пока обработчики HTTP их читают. Это гонка данных, которую go test -race находит сразу, а на проде она проявляется редкими битыми значениями или падением с concurrent map read and map write.
При ошибке источника обнуляют кэш. Сервис, который мог бы отдавать чуть устаревшие данные, начинает отдавать ошибки, и сбой одной зависимости превращается в падение всего сервиса.