DEV Community

Cover image for Active Record Transactions in Ruby on Rails
Sulman Baig
Sulman Baig

Posted on • Updated on

Rails Transaction Active Record Transactions in Ruby on Rails

I have learned a lot by reading the API docs at  Ruby on Rails Active Record Transaction .

Transactions are protective blocks where SQL statements are only permanent if they can all succeed as one atomic action.

The classic example is a transfer between two accounts where you can only have a deposit if the withdrawal succeeded and vice versa.

Transactions enforce the integrity of the database and guard the data against program errors or database break-downs. So basically you should use transaction blocks whenever you have a number of statements that must be executed together or not at all.

Examples

1. Withdrawal and Deposit:

ActiveRecord::Base.transaction do
  david.withdrawal(100)
  mary.deposit(100)
end
Enter fullscreen mode Exit fullscreen mode

So, while running the withdrawal method of the user model, if an error comes then the next method of user deposit is not called.

Similarly, if the withdrawal of David is done but an error comes in the deposit method of Mary then changes occurred during David’s withdrawal method will be rollback so the money will not be transferred at all.

2. Object Duplication:

I normally use Active Record Transactions when I am about to duplicate an instance that also has a relation of has_many with other model instances.

class Post < ApplicationRecord
  ...
  def duplicate!
    ActiveRecord::Base.transaction do
      post_dup = self.dup
      post_dup.save!
      comments.find_each do |comment|
        comment_dup = comment.dup
        comment_dup.post_id = post_dup.id
        comment_dup.save!
      end
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

So, the duplicated post doesn’t get saved until unless all the comments of the post are duplicated and save with duplicate post id.

Callbacks of Active Record Transaction

Active Record Transactions have two callbacks after_commit, and after_rollback.

after_commit is called when the transaction is completed successfully.

after_rollback is called when the transaction is unsuccessful.

Models are children of Application Record:

Transactions are per-database connection, not per-model.

As the transaction is the part of ActiveRecord::Base so all models can access transactions using class or instance. For Example, below are all valid snippets:

Account.transaction do
  balance.save!
  account.save!
end
Enter fullscreen mode Exit fullscreen mode
balance.transaction do
  balance.save!
  account.save!
end
Enter fullscreen mode Exit fullscreen mode

Rails 6 multiple database connection issues:

A transaction acts on a single database connection. If you have multiple class-specific databases, the transaction will not protect interaction among them. One workaround is to begin a transaction on each class whose models you alter:

Student.transaction do
  Course.transaction do
    course.enroll(student)
    student.units += course.units
  end
end
Enter fullscreen mode Exit fullscreen mode

This is a poor solution, but fully distributed transactions are beyond the scope of Active Record.

Top comments (0)