> Что такое негативный кэш и почему он вреден (Go)

Уровень: junior · Роль: backend · Категория: Технические вопросы

Компании: VK

Стек: Go

> Пример ответа

Короткий ответ

Негативный кэш - это хранение информации об отсутствии ресурса (например, 404 или ошибки DNS) на некоторое время. Он полезен для снижения нагрузки на сервер, но вреден, когда данные появляются быстро: клиенты продолжают получать устаревший "отрицательный" ответ, хотя ресурс уже доступен. В Go это часто проявляется при кэшировании ошибок от внешних API или БД - закэшированная ошибка блокирует восстановление сервиса.

Подробное объяснение

Негативный кэш работает по принципу: "если запрос завершился неудачей, запомни это на N секунд". Это уменьшает количество повторных запросов к недоступному ресурсу, экономя ресурсы и предотвращая каскадные сбои.

Вред возникает из-за трёх факторов:

  • Устаревание данных: если ресурс восстановился через секунду, а TTL негативного кэша - 5 минут, клиенты всё это время получают ложный отрицательный ответ.
  • Маскировка проблем: негативный кэш скрывает реальное состояние системы - мониторинг видит стабильные ошибки, хотя причина уже устранена.
  • Накопление ошибок: при частичных сбоях (например, один из нескольких реплик БД) негативный кэш может "запомнить" ошибку от одной реплики и игнорировать здоровые.

В Go типичный сценарий - кэширование ошибок от http.Client или database/sql. Если вы сохраняете ошибку в sync.Map или Redis с TTL, вы рискуете тем же.

На практике

Правильный подход - использовать негативный кэш только для стабильных отрицательных ответов (например, 404 для неизменяемых ресурсов) и с коротким TTL. Для временных ошибок (5xx, network timeout) лучше применять retry с экспоненциальной задержкой, а не кэширование.

В Go стоит различать:

  • Ошибки бизнес-логики (404, 409) - можно кэшировать с умеренным TTL.
  • Инфраструктурные ошибки (connection refused, timeout) - кэшировать нельзя, либо TTL ≤ 1 секунды.

Также важно инвалидировать негативный кэш принудительно: например, при успешном фоновом health-check или при получении сигнала от источника данных.

Пример кода

GO
package main
import (
"sync"
"time"
)
type NegativeCache struct {
mu sync.Mutex
items map[string]cacheItem
}
type cacheItem struct {
expiresAt time.Time
}
func (c *NegativeCache) IsNegative(key string) bool {
c.mu.Lock()
defer c.mu.Unlock()
item, ok := c.items[key]
if !ok {
return false
}
if time.Now().After(item.expiresAt) {
delete(c.items, key)
return false
}
return true
}
func (c *NegativeCache) SetNegative(key string, ttl time.Duration) {
c.mu.Lock()
defer c.mu.Unlock()
c.items[key] = cacheItem{expiresAt: time.Now().Add(ttl)}
}
func fetchWithCache(key string, fetch func() (interface{}, error)) (interface{}, error) {
// Проверяем негативный кэш
if negativeCache.IsNegative(key) {
return nil, errors.New("cached negative")
}
data, err := fetch()
if err != nil {
// Кэшируем только временные ошибки, TTL короткий
negativeCache.SetNegative(key, 500*time.Millisecond)
return nil, err
}
return data, nil
}

Как отвечать на собеседовании

Начните с определения, затем сразу переходите к trade-off: "негативный кэш снижает нагрузку, но создаёт риск устаревших отрицательных ответов". Приведите пример из практики: "в Go при работе с внешним API я бы кэшировал 404, но не 500, и с TTL не больше секунды". Упомяните инвалидацию и мониторинг - это покажет системное мышление. Если спросят про конкретные структуры - скажите про sync.Map или Redis, но подчеркните, что главное - политика TTL и типы ошибок.

Что проверяет интервьюер

  • Понимание компромиссов кэширования: скорость vs актуальность.
  • Умение различать типы ошибок (временные vs постоянные).
  • Знание Go-специфики: конкурентный доступ, sync.Map, context для отмены.
  • Способность предложить решение с учётом восстановления сервиса.

Типичные ошибки

  • Кэшировать все ошибки без разбора - включая 5xx и network timeout.
  • Использовать слишком большой TTL (минуты) для негативных ответов.
  • Не предусматривать принудительную инвалидацию при восстановлении источника.
  • Путать негативный кэш с обычным кэшем данных - у них разные стратегии очистки.
  • В Go забывать про гонки данных при работе с map - нужен sync.Mutex или sync.Map.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью