Why defer a Rollback?

Viewed 7495

I have started using Go for a web-service and have some database interaction (surprise!!!) and I have found this example:

tx, err := db.Begin()
if err != nil {
    log.Fatal(err)
}
defer tx.Rollback()
stmt, err := tx.Prepare("INSERT INTO foo VALUES (?)")
if err != nil {
    log.Fatal(err)
}
defer stmt.Close() // danger!
for i := 0; i < 10; i++ {
    _, err = stmt.Exec(i)
    if err != nil {
        log.Fatal(err)
    }
}
err = tx.Commit()
if err != nil {
    log.Fatal(err)
}
// stmt.Close() runs here!

From http://go-database-sql.org/prepared.html

The example is well formulated an easy to understand. However, it leaves me with an unanswered question. Why defer the transaction Rollback call?

Why not just do the following:

err := tx.Commit()

if err != nil {
    log.Error(err)
    tx.Rollback()
}

Would defer tx.Rollback() not always attempt a rollback? Even if tx.Commit() was a success, or have I misunderstood something about defer?

2 Answers

The important thing is that if you defer tx.Rollback() you are sure that it will be called even if you do an early return and the "trick" is that calling tx.Rollback() on a committed transaction will not actually do the rollback, because once a transaction is committed, it's committed and there is no way to roll it back :) So this is a neat trick on how to keep the code simple.

The example is a little bit misleading. It uses log.Fatal(err) for error handling. You wouldn't normally do that, and instead return err. So the deferred rollback is there to ensure that the transaction is rolled back in case of an early return.

Related