What are Rails Transaction Blocks and How to Use Them

Daniel Pericich
4 min readAug 20, 2021
Photo by Stephen Dawson on Unsplash

Databases are the backbone of any data intensive app. They keep track of who did what and when. While databases are usually great at recording the order of events, just like human memories, they are prone to lapses.

A set of instructions sent to a database can be interrupted and ruined for many reasons. While something like redoing a draft of an article may be a minor inconvenience, a botched bank transfer could lead to the loss of millions of dollars for a company. Because of this, computer scientists introduced the concept of transaction blocks.

What is a transaction block?

In short, a transaction block is a wrapper around a set of instructions that will either fully execute, or roll back any partially completed instructions. The most common example of a transaction block in use is a transfer between two bank accounts.

For this example, imagine that you are working at a bank. You have two customers whose names are Louis and Pierre. Louis sold his bike to Pierre for $50 and would like to have the money transferred to his bank account. We need to write a very simple algorithm to facilitate this transaction. The algorithm follows these steps:

  1. Retrieve Pierre’s bank account record
  2. Remove $50 from Pierre’s account
  3. Retrieve Louis’ bank account record
  4. Add $50 to Louis’ account

This set of instructions is simple enough and our program is soon running. Unfortunately after we remove the $50 from Pierre’s account, the whole system crashes. To our horror we find out that the intern was trying to charge their phone and unplugged our servers.

After 10 minutes of rebooting your equipment, you find that that your program is not running anymore. You check the balances of Pierre and Louis’s accounts and find that Louis’s balance has not changed, but Pierre’s is $50 short.

If you run your program again, Pierre will have spent $100 for the bike: $50 transferred to Louis and $50 that disappeared completely. This isn’t right, but how do we prevent these situations from happening? This is where transaction blocks come in.

A transaction block is very simply a wrapper that you put around a set of database methods. Here is what our code would look like with a transaction block:

Figure 1. Ruby on Rails Transaction Block

Transaction blocks have an all or nothing approach to database modifications. Either everything in the block runs and the changes to the database are persisted, or nothing is persisted if the code is interrupted by errors or system shutdowns. This prevents partial transactions from causing untraceable issues.

While transactions blocks are simple to implement, there are a number of issues that can arise if they are used incorrectly.

Inconsistencies Between Databases and Global Variables

After you’ve implemented a transaction block once, you find that they are not all that complicated. Though the idea of rolling back database operations is simple enough, there are still some common bugs that can trip you up.

The first, and one of the most common issues that can be caused with transaction blocks is rolling back non database operations. Transaction blocks roll back database transactions, and they do this very well. However, if you perform an action like updating a counter, or reassigning values for use elsewhere in your class or method, these will not be rolled back if the transaction block is exited early.

Along with issues related to normal methods, a transaction block also may cause issues when you try to work with multiple databases. A project I was recently working on separated out their order records from their user records. Both were housed in separate projects, with one making calls to the other.

While it is alright to setup methods like this, it is important to separate API calls from your internal database transactions. What I found was that when a method failed inside a transaction block, the internal database operations would rollback, but there is no way to rollback the external calls.

The transaction block is only able to act on the database that its code houses. Because of this it is very important to not adjust global variables or make external api calls or separate database modifications from inside a transaction block.

Conclusion

Transaction blocks are the perfect tool to ensure that you have consistency in your database regardless of external factors. It is important to remember that while they are great at tracking and rolling back internal database changes, they do not rollback global variable changes, API calls or external database changes. Let me know in the comments how you have used transaction blocks or how you plan to use them in the future.

--

--

Daniel Pericich

Former Big Beer Engineer turned Full Stack Software Engineer