Вступить в клуб →
сложнаявопросКэш, очереди и фоновые задачи

Списания в Redis теряются при восьми воркерах

Баланс бонусов хранится в Redis, списание сделано так:

$balance = (int) Redis::get("balance:{$userId}");
$balance -= $amount;
Redis::set("balance:{$userId}", $balance);

Очередь обрабатывают восемь воркеров параллельно. При активных пользователях часть списаний исчезает: баланс выше, чем должен быть по сумме проведённых операций.

В чём причина?

  • Redis однопоточный, поэтому гонок здесь быть не может: списания теряются из-за отсутствия персистентности и сброса данных на диск с задержкой
  • Причина в приведении к int: значение приходит из Redis строкой, и при пустом ключе часть списаний обнуляется приведением к нулю
  • Чтение, изменение и запись — три отдельные операции: между get и set другой воркер записывает своё значение, и оно затирается. Нужна атомарная операция или блокировка по ключу
  • Достаточно уменьшить число воркеров до двух: вероятность одновременного обращения к одному ключу станет пренебрежимо малой

🔒 Проверка ответа — для участников клуба

  • Проверка ответа
  • Подсказка, если застряли
  • Разбор с объяснением, почему так
  • Прогресс по всем задачам и виртуальные собеседования
Зарегистрироваться →

Регистрация занимает минуту

Другие задачи раздела