Transaction Management in Golang

Hello Everyone here, I am working with Golang from last month. I want to know how to deal with transaction management. It is like in the MVC architecture, if Service Layer is performing two related operations in the single request and if any one of fails should rollback the whole transaction i.e. roll backing the changes by following DO Nothing OR Everything principal.

So If anybody knows how to deal like this, kindly reply. Your replies are valuable.

From your other post, you seem to be using GORM. GORM supports transactions:

http://jinzhu.me/gorm/advanced.html#transactions

Yes Mr. nathankerr, you are right. GORM is supporting transaction management. In Java Spring Framework, DataSourceTransactionManager/HibernateTransactionManager, JTA, third party atomikos tools are there that deals with Transaction management alongwith AOP. But here, we need to handle tx manually. Suppose , I while dealing with 2 database operations in single request and if error occurs in 2nd one then it should rollback the whole transaction. So I’m asking is there any approach like same.

Yes. Use Go to group the two operations in a single transaction.

How should I do this? Could you give me some sample code or any example?

Resourcespanning transaction management is a hard problem in IT.

Consider this:

  1. Write file to clouddrive
  2. Write metadata about that file to database
  3. Writing to DB fails due to network outage
  4. You can’t rollback storage to clouddrive, because you have network outage.

Of course there are mechanisms available to postpone the transaction (or its backroll) until network is available again. But until those postponed measures have been taken, the world is in inconsistent state for other observers/instances of your server.

The example code from the docs is ok:

func CreateAnimals(db *gorm.DB) err {
  tx := db.Begin()
  // Note the use of tx as the database handle once you are within a transaction

  if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
     tx.Rollback()
     return err
  }

  if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
     tx.Rollback()
     return err
  }

  tx.Commit()
  return nil
}

This will either create both animals or none of them.

1 Like

Hello Mr. Norbert Melzer, I can’t get what you are saying. Kindly give me the examples .

There is a simplified example in my Post…

Also there are even resources available which you can’t roll back at all… Pushing a message to a MessageQueue for example… If it has been sent, then you can’t take it back. So if another failable step of the transaction relies on the message beeing sent, but does fail on itself, your message sent to the MQ does suggest to any receiver, that the following step is already done or happens at least very soon.

Hi Mr. Nathankerr, Thanks for sharing this example. Here, you are doing this Tx activities in DAO layer itself. But I want to do in Service Layer. Hope you understand what I am saying.

1 Like

From looking at the code from your other post, I recommend thinking of GORM itself as your DAO.

Instead of calling:

dao.SaveUser(user)

Call GORM directly:

Db.Save(&user)

This will reduce the amount of code you need to write and give you access to GORM’s transactions.

Hello Mr. nathankerr, I’m calling this dao.SaveUser(user) from Service Layer in order to follow MVC architecture.

I’m not sure how a DAO or Service Layer fit into your version of MVC (Model, View, Controller)…

From the code in your other post, you can call GORM directly with:

dao.Db.Save(&user)

Seems to me like the biggest problem you are having is trying to bang Go into a Java shaped hole, and thats not what it’s intended for. The traditional MVC isn’t necessary if you think about how your application operates. This one in particular is already separated by client side, server side, and database access. GORM handles all database access after you have handled requests from the client on your server, so any scrubbing/data validation/etc can be handled by the server before it commits these to the database. You don’t have to inject a metric ton of boilerplate to conform to MVC as Java understands it, welcome to Go.

I’d check for errors on both tx.Rollback() and tx.Commit() myself. You don’t want to report success if the Commit() fails because the database transaction log is full.

Yet another case where sample code from official documentation (for the package) could be better…

If tx.Rollback returns an error, then the code needs to deal with two errors instead of one. My article, https://pocketgophers.com/handling-errors-in-defer/, includes an approach for doing so.

2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.