Урок по треку Node.js и бэкенд 8 мин

Кеш и очереди в Node-бэкенде

Redis, стратегии кеширования и инвалидации, очереди задач, ретраи и идемпотентность.

Два самых частых способа разгрузить сервис: кеш (не считать и не читать повторно то, что уже есть) и очередь (не делать в ответе пользователю то, что можно сделать потом). Оба приносят свои проблемы: устаревшие данные и повторную обработку.

Где кешировать

  • В памяти процесса (Map, lru-cache): самый быстрый, но у каждого экземпляра свой, пропадает при рестарте, растёт вместе с памятью процесса. Ограничивай размер.
  • Redis: общий для всех экземпляров, данные в памяти, доступ за доли миллисекунды по сети. Стандартный выбор.
  • HTTP-кеш и CDN: для публичных ответов, через Cache-Control.

Стратегии

Cache-aside (ленивое кеширование), самая частая:

async function getProduct(id) {
  const key = `product:${id}`;
  const cached = await redis.get(key);
  if (cached) return JSON.parse(cached);
  const product = await db.product.findUnique({ where: { id } });
  await redis.set(key, JSON.stringify(product), 'EX', 300); // TTL 5 минут
  return product;
}

При изменении товара ключ удаляют, а не обновляют: так меньше шансов записать в кеш устаревшее значение при гонке двух запросов.

  • Write-through: при записи обновляем и базу, и кеш сразу.
  • Write-behind: пишем в кеш, в базу позже пачкой. Быстро, но при падении можно потерять данные.

TTL нужен почти всегда: даже если инвалидация где-то забыта, данные устареют не навсегда.

Проблемы кеша

  • Инвалидация: при изменении данных нужно найти и сбросить все ключи, где они лежат. Чем больше производных ключей (списки, агрегаты), тем сложнее.
  • Cache stampede (лавина): популярный ключ истёк, и тысяча запросов одновременно идёт в базу. Лечится блокировкой на пересчёт (SET key NX EX), объединением одинаковых запросов в один промис, ранним фоновым обновлением.
  • Одновременное истечение: тысячи ключей с одинаковым TTL истекают в одну секунду. Добавляй к TTL случайный разброс (jitter).
  • Память Redis: когда она кончается, поведение задаёт maxmemory-policy. По умолчанию это noeviction: записи начинают отклоняться с ошибкой. Для кеша обычно ставят allkeys-lru или allkeys-lfu.

Redis: что нужно знать

Команды Redis выполняются по одной (основной поток обрабатывает их последовательно), поэтому каждая команда атомарна. INCR безопасен для счётчиков и rate limit. SET key value NX EX 30 атомарно ставит ключ, только если его нет, это основа простой распределённой блокировки. Долгие команды вроде KEYS * блокируют весь Redis, вместо них SCAN.

Очереди

Очередь отделяет приём задачи от её выполнения. Пользователь получил ответ «заказ принят», а письмо, PDF и интеграции делаются фоном воркерами.

  • BullMQ: очереди задач поверх Redis, популярны в Node. Повторы, задержки, приоритеты, ограничение параллелизма.
  • RabbitMQ: брокер сообщений с маршрутизацией и подтверждениями.
  • Kafka: распределённый журнал событий, хранит сообщения и позволяет перечитывать, высокая пропускная способность.

Надёжность обработки

Почти все очереди дают доставку at-least-once: если воркер упал до подтверждения, задача придёт снова. Значит, обработчик должен быть идемпотентным: повтор не создаёт второй платёж или второе письмо. Приёмы: уникальный ключ операции в базе, проверка «уже сделано», upsert.

Повторы делают с экспоненциальной задержкой и разбросом (1 с, 2 с, 4 с...), чтобы не добивать упавший сервис. Задачи, исчерпавшие попытки, уходят в dead letter queue для разбора.

Transactional outbox: если нужно и записать в базу, и отправить событие, запись события кладут в таблицу outbox в той же транзакции, а отдельный процесс публикует её в брокер. Иначе возможна запись без события или событие без записи.

Частые ошибки

  • Кеш без TTL и без лимита размера.
  • Обновлять кеш вместо удаления при изменении.
  • Неидемпотентные обработчики задач.
  • Бесконечные мгновенные повторы.
  • KEYS * в проде.

Что спросят на собесе

  • Какие стратегии кеширования знаешь и как инвалидировать кеш.
  • Что такое cache stampede и как его избежать.
  • Зачем очередь и что значит at-least-once.
  • Как сделать обработчик идемпотентным.
  • Чем Redis-очередь отличается от Kafka.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.