rails, database,

Swapnil Gourshete Swapnil Gourshete Follow Oct 29, 2023 · 4 mins read
What is Transaction? How to do it in Rails?
Share this

What is transaction?

Transaction is the smallest non-dividable piece of code. It can be described as the entity which we cannot be modularised again into smaller modules such that it will be run in a binary form - either it is executed completely or it is not executed at all.

In terms of business logic, transaction can be described as a group of few operations which are necessarily need to be performed together at all cost. Let’s try to understand through an example. In digital banking a transfer of money would be successful only if the money is debited from the sender’s account and same amount of money is credited into receiver’s bank account. The transaction would be incomplete if any of these two statements are not successful.

How transactions can be implemented rails?

ActiveRecord has a method called ‘transaction’ which can be called like below space -

ActiveRecord::Base.transaction do 
  {code block}
end

As this is an ActiveRecord implementation, it ensures every write or update to the database is either completely committed or none of the database updates are committed.

Let's see through an example.

ActiveRecord::Base.transaction do 
  user = User.first
  user.update!(status: 1)
  user.products.update_all(frozen: true) 
end

Here we are trying to update the User and associated Product records in the database and these are wrapped inside a transaction. The console log for this code will be like this -

transaction1-log

We can see that both the database updates are sucessful.

Now,

let’s try to play with transaction block. As we previously mentioned it will either save all changes or will not save any change. Let’s test it.

For this use case we will need a code which will throw an error and causes unsuccessful completion of code. We can make use of divide by 0 exception by simply putting a statement without complicating it. Here is how the updated code block will look like -

ActiveRecord::Base.transaction do 
  user = User.first
  user.update!(status: 0)
  user.products.update_all(frozen: false)

  ### will throw ZeroDivisionError
  foo = 10/0
end

Let’s try to execute this code block. And here is the result -

transaction2-rollback-log

As you can see from the bottom of the log, this code has thrown divided by zero exception and as a result of which, database has rolled back the transaction. Meaning both the updates first to the users table and second to the products table are not saved to the database. So both the tables users and products are now at the same state they were before running the transaction block.

Curious about foo? Variable vs DB save?

What happens to the value of foo? As it is not a database entity but a simple local very able how transaction deals with it? Let’s find out.

foo = 1
ActiveRecord::Base.transaction do 
  foo = 2

  user = User.first
  user.update!(status: 0)
  user.products.update_all(frozen: false)

  ### will throw ZeroDivisionError
  foo = 10/0
end
foo

Let’s try to execute this code block. And here is the result -

transaction-3-foo

Here you can see the value of food is equal to last update value within the transaction block. Foo has not rolled back its value to pre-transaction block value. By this example we can conclude local variable values are not ruled back even if transaction fails.

More on Transactions in Rails -

  • Transactions are executed on a single database connection. Managing transaction in a fully distributed system is out of scope of the active record.

  • They can be called on any Model or an instance of Model object. like -

### 1. Model
User.transaction do
  user = User.first
  user.update!(status: 0)
end


### 2. Model instance
user = User.first
user.transaction do
  user.update!(status: 0)
end
  • It is not mandatory to update only records from the model which initiated the transaction block but we can also update records from other Models. e.g. -
User.transaction do
  user = User.first
  user.update!(status: 0)

  user.picture.update!(title: 'Lorem ipsum')
end

References

  • rails repo - activerecord/lib/active_record/

Swapnil Gourshete
Written by Swapnil Gourshete Follow
Hi I am Swapnil, a Software Engineer and computer science enthusiastic