Вы хорошо объяснили проблему гонки и роль await. А как бы вы реализовали защиту этой составной операции с помощью асинхронного лока в FastAPI?

Создайте один общий asyncio.Lock для защищаемого состояния и поместите всю проверку и изменение внутрь async with lock. Новый Lock на каждый запрос не защитит ничего, потому что запросы не будут разделять блокировку.

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

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

Создайте один общий asyncio.Lock для защищаемого состояния и поместите всю проверку и изменение внутрь async with lock. Новый Lock на каждый запрос не защитит ничего, потому что запросы не будут разделять блокировку.

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

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

Создайте один общий asyncio.Lock для защищаемого состояния и поместите всю проверку и изменение внутрь async with lock. Новый Lock на каждый запрос не защитит ничего, потому что запросы не будут разделять блокировку.

Учебный вариант для одного процесса и event loop:

import asyncio

class Account:
    def __init__(self, balance):
        self.balance = balance
        self.lock = asyncio.Lock()

    async def withdraw(self, amount):
        if type(amount) is not int or amount <= 0:
            raise ValueError('Invalid amount')
        async with self.lock:
            if self.balance < amount:
                raise ValueError('Insufficient funds')
            self.balance -= amount
            return self.balance

Экземпляр Account должен быть общим для запросов. Такой лок не координирует разные FastAPI workers; для баланса в БД нужна атомарная операция БД. Сетевую работу без необходимости внутри критической секции не выполняют.

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

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

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

Подробнее