Два одновременных списания: потерянное обновление баланса
Списание средств реализовано так:
var acc = await db.Accounts.FirstAsync(a => a.Id == id);
acc.Balance -= amount;
await db.SaveChangesAsync();
Баланс 1000. Пришли два параллельных запроса на списание по 100 каждый, оба успели прочитать строку до того, как любой из них сохранил изменения. В базе оказалось 900.
Как корректно устранить проблему?
- Достаточно обернуть код в транзакцию: сама транзакция не даст двум запросам одновременно прочитать и изменить одну строку, второй дождётся первого
- Выполнять вычитание атомарно на стороне базы, блокировать строку при чтении или добавить токен параллелизма
- Ничего делать не нужно: EF Core сам обнаружит конфликт и выбросит DbUpdateConcurrencyException
- Перечитывать сущность прямо перед SaveChanges — тогда второй запрос увидит уже изменённый баланс
