Выбор ответа Middle Разборы систем обновлено

Гонка в rate limiter

Вопрос

Rate limiter реализован так. Что в нём не так?

const key = `rl:${userId}:${currentMinute}`;
const count = Number(await redis.get(key)) || 0;
if (count >= 100) return reply.code(429).send();
await redis.set(key, count + 1, 'EX', 60);

Попробуйте сами

Выберите вариант, и откроется разбор.

Показать ответ и разбор

Коротко

Правильный ответ: Между GET и SET проходят параллельные запросы, нужен INCR. Redis выполняет каждую команду атомарно, но GET и SET это две команды, между ними успевают выполниться команды других клиентов.

Варианты ответа

  1. Ничего: Redis однопоточный, значит этот код атомарен
  2. Нужно убрать TTL, он сбрасывает счётчик посреди минуты
  3. Между GET и SET проходят параллельные запросы, нужен INCRправильный ответ
  4. Ключ не должен содержать минуту, иначе окно не скользит

Разбор

Redis выполняет каждую команду атомарно, но GET и SET это две команды, между ними успевают выполниться команды других клиентов. Десять параллельных запросов прочитают count = 99 и все пройдут. INCR атомарно увеличивает и возвращает новое значение; EXPIRE ставят при первом увеличении, а надёжнее всё сделать в одном Lua-скрипте. Минута в ключе как раз и задаёт фиксированное окно, а TTL нужен, чтобы старые ключи не копились.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.