Skip to content

go.transaction_boundary

Correctness High

Detects database operations spanning multiple queries without transaction boundaries.

Without transactions:

  • Partial updates — Some changes commit, others fail
  • Data inconsistency — Concurrent reads see intermediate states
  • Lost writes — Overwrites happen between read and update
  • Recovery issues — Can’t rollback partial operations
// ❌ Before (no transaction)
func transfer(db *sql.DB, from, to int, amount float64) error {
_, err := db.Exec("UPDATE accounts SET balance = balance - ? WHERE id = ?", amount, from)
if err != nil {
return err
}
// If this fails, money is lost!
_, err = db.Exec("UPDATE accounts SET balance = balance + ? WHERE id = ?", amount, to)
return err
}
// ✅ After (with transaction)
func transfer(db *sql.DB, from, to int, amount float64) error {
tx, err := db.Begin()
if err != nil {
return err
}
defer tx.Rollback()
_, err = tx.Exec("UPDATE accounts SET balance = balance - ? WHERE id = ?", amount, from)
if err != nil {
return err
}
_, err = tx.Exec("UPDATE accounts SET balance = balance + ? WHERE id = ?", amount, to)
if err != nil {
return err
}
return tx.Commit()
}
  • Multiple db.Exec() calls without db.Begin()
  • Missing tx.Commit() or tx.Rollback()
  • Related writes without transaction wrapper

Unfault wraps operations in transactions:

tx, err := db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer tx.Rollback()
// ... operations
return tx.Commit()