Database transaction opened without a deferred rollback
What this failure means
A database transaction is opened and committed in the same function with no
defer tx.Rollback() guard, so if an error path returns early the transaction
is silently left open, holding connections and locks.
Diagnosis
A db.Begin() or db.BeginTx() call is followed by tx.Commit() in the
same function, but there is no defer tx.Rollback() to release the
transaction on error paths.
The idiomatic Go pattern defers a rollback immediately after a successful
Begin:
tx, err := db.Begin()
if err != nil { return err }
defer tx.Rollback() // safe no-op if Commit already succeeded
Without the deferred rollback:
- Any early return on an error before
Commitleaves the transaction open until the database connection times out. - Under concurrent load, leaked transactions exhaust the connection pool.
- Table locks held by uncommitted transactions block other writers.
Fix steps
- Add
defer tx.Rollback()immediately after a successfulBegin:tx, err := db.BeginTx(ctx, nil) if err != nil { return fmt.Errorf("begin tx: %w", err) } defer tx.Rollback() // always deferred; no-op after a successful Commit // ... mutations ... return tx.Commit() - Return the error from
Commitso the caller knows if it failed. - Avoid capturing
txin a closure that may outlive the deferred rollback.
Validation
- Run
faultline inspect .from the repository root and confirm this source finding is absent or intentionally mitigated. - Test that an error on any path before
Commitreleases the connection cleanly without exhausting the pool.
Why it matters
Unreleased transactions hold database connections and table locks. Under load, a handful of leaked transactions can exhaust the connection pool and cause cascading failures across every service that shares the same database.
Try it locally
make test
rg -n 'db.Begin|db.BeginTx' .
make test
go vet ./...
How Faultline detects it
Use faultline explain missing-transaction-rollback to see the full playbook.
faultline analyze build.log
faultline explain missing-transaction-rollback
Generated from playbooks/bundled/source/missing-transaction-rollback.yaml. Do not edit directly.