HTTP-вызов внешнего провайдера внутри открытой транзакции
Обработка платежа выглядит так:
tx, _ := db.BeginTx(ctx, nil)
defer tx.Rollback()
_, _ = tx.ExecContext(ctx, `UPDATE accounts SET balance = balance - $1 WHERE id = $2`, amount, accID)
resp, err := provider.Charge(ctx, req) // внешний HTTP, обычно 300 мс, при деградации до 10 с
if err != nil {
return err
}
_, _ = tx.ExecContext(ctx, `INSERT INTO payments (...) VALUES (...)`)
return tx.Commit()
При замедлении провайдера сервис перестаёт отвечать целиком, в логах — ожидание свободного соединения к БД. Что здесь основная причина?
- Транзакция удерживает соединение и блокировки строки счёта всё время внешнего вызова — растут очереди и исчерпывается пул
- Проблема снимается уровнем изоляции: на Read Committed блокировки строк не удерживаются до конца транзакции
- Причина в `defer tx.Rollback()`: он держит транзакцию открытой даже после успешного `Commit` и не отдаёт соединение
- Достаточно поднять `statement_timeout` на сервере БД, чтобы ожидания перестали накапливаться в очереди к пулу
