Active Record Transactions in Ruby on Rails

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

Active Record Transactions in Ruby on Rails

The Original post is at https://sulmanweb.com/blog/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 numerous 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

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

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
balance.transaction do
  balance.save!
  account.save!
end

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

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