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.
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.
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.
Active Record Transactions have two callbacks
after_commit is called when the transaction is completed successfully.
after_rollback is called when the transaction is unsuccessful.
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
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.