@app.post("/transfer")

Перевод небезопасен: SQL строится интерполяцией, деньги представлены float, а списание и зачисление не объединены в транзакцию. Параллельные запросы могут нарушить баланс.

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

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

Перевод небезопасен: SQL строится интерполяцией, деньги представлены float, а списание и зачисление не объединены в транзакцию. Параллельные запросы могут нарушить баланс.

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

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

Условие

@app.post("/transfer") async def transfer( from_user: str = Body(), to_user: str = Body(), amount: float = Body(), ): conn = await get_connection()

row = await conn.fetchrow(
    f'select id, balance from "user" where id = \'{from_user}\''
)

if row["balance"] < amount:
    raise HTTPException(status_code=400, detail="Insufficient funds")

await conn.execute(
    f'UPDATE "user" SET balance = balance - {amount} WHERE id = \'{from_user}\''
)
await conn.execute(
    f'UPDATE "user" SET balance = balance + {amount} WHERE id = \'{to_user}\''
)

Ответ

Перевод небезопасен: SQL строится интерполяцией, деньги представлены float, а списание и зачисление не объединены в транзакцию. Параллельные запросы могут нарушить баланс.

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

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

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

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

Подробнее