Вступить в клуб →
средняявопросPostgreSQL, JPA и производительность запросов

Два инстанса списывают бонусы: почему баланс сходится неверно

Списание бонусов реализовано так (уровень изоляции по умолчанию, READ COMMITTED):

@Transactional
public void withdraw(Long accountId, int amount) {
    Account acc = repository.findById(accountId).orElseThrow();
    acc.setBalance(acc.getBalance() - amount);
    repository.save(acc);
}

При балансе 100 два инстанса сервиса одновременно списали по 30. В базе осталось 70 вместо 40. Что произошло и как это чинить?

  • Проблема в кэше первого уровня Hibernate: достаточно вызвать flush перед сохранением, и запись пойдёт в базу сразу
  • Потерянное обновление: оба прочитали 100 и записали 70. Нужен UPDATE по текущему значению колонки, блокировка строки или версионирование
  • Достаточно пометить метод synchronized: тогда списания выстроятся в очередь и баланс сойдётся
  • @Transactional уже защищает от такого, значит в коде есть повторный вызов withdraw для одной операции

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

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

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

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