Гонка read–modify–write при списании баланса

Здесь гонка read–modify–write: два запроса могут проверить один баланс и перезаписать результаты друг друга. Проверку остатка и уменьшение нужно выполнять атомарно, а сумму валидировать.

Короткий ответ

Что ответить на собеседовании

Здесь гонка read–modify–write: два запроса могут проверить один баланс и перезаписать результаты друг друга. Проверку остатка и уменьшение нужно выполнять атомарно, а сумму валидировать.

Подробный разбор

Ответ с пояснениями

Условие

def withdraw(user_id, amount): user = User.objects.get(id=user_id) if user.balance < amount: raise InsufficientFundsError() user.balance -= amount user.save()

Ответ

Здесь гонка read–modify–write: два запроса могут проверить один баланс и перезаписать результаты друг друга. Проверку остатка и уменьшение нужно выполнять атомарно, а сумму валидировать.

Для суммы в целых минимальных единицах:

from django.db.models import F

def withdraw(user_id, amount):
    if type(amount) is not int or amount <= 0:
        raise ValueError("Некорректная сумма")
    updated = User.objects.filter(
        id=user_id, balance__gte=amount
    ).update(balance=F("balance") - amount)
    if not updated:
        raise InsufficientFundsError()

Пример не различает отсутствующего пользователя и недостаточный остаток. Для реальной операции нужны проверка полномочий, идемпотентный идентификатор, журнал проводок и общая транзакция с ним. update() не вызывает модельный save().

Источник: docs.djangoproject.com.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее