Перевод небезопасен: SQL строится интерполяцией, деньги представлены float, а списание и зачисление не объединены в транзакцию. Параллельные запросы могут нарушить баланс.
@app.post("/transfer")
Перевод небезопасен: 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 нельзя индексировать без проверки.