Transaction = unit of consistency, not unit of speed
Every HTTP request usually wraps its database work in one transaction. The transaction's job is "all of this either lands together or doesn't" — it is not a performance feature. Long-running transactions hold locks, accumulate dead tuples, block VACUUM, and create contention for everyone else.
Five rules that prevent most production database fires
- Keep transactions short. Open them as late as possible, commit them as soon as possible. Don't make external HTTP calls inside an open transaction.
- Don't hold a transaction across user input. If you BEGIN, then wait for the user to fill out a form, then COMMIT — you've held a row lock for minutes.
- Use READ COMMITTED unless you have a reason not to. It's the default for good reason.
- Always handle SerializationFailure if you use REPEATABLE READ or SERIALIZABLE. Retry with backoff.
- Avoid
SELECT FOR UPDATEacross multiple round trips. If you must lock-then-think-then-update, do it in one stored procedure or one statement.
The web request transaction template
Every framework has a clean way to wrap a request in a transaction. Use it. Custom BEGIN/COMMIT in route handlers is how transaction leaks happen.