Ruby on Rails
HowToUseTransactions

Transactions in ActiveRecord group together a number of changes to the underlying database in such a way that either all the changes are applied or none of them are applied. These are single database transactions not XA-style multiple-resource distributed systems transactions. When you use the Transactions API, the system is really just using the underlying database SQL BEGIN...COMMIT.

Note that the underlying database has to support transactions. For MySQL that means using the InnoDB storage engine. (However, note that InnoDB doesn’t support full-text indexing like MyISAM does, so make sure you don’t need it)

Every ActiveRecord object has a transaction method, which can take a block. All database activity within the block will occur in the context of a BEGIN...COMMIT. If the block ends normally, the changes are committed. If an exception is raised within the block, any changes made are rolled back and the transaction aborted.

E.g.


Account.transaction do
balance.save!
account.save!
end

See http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html for more details.

ActionRecord uses the same transaction technique when saving or destroying dependent rows in collections.

Using transactions between two or more databases

If you want to do transactions over multiple databases, the best you can do is start a transaction for every database.

Account.transaction do
  User.transaction do
    yada_yada
  end
end

You only need to do this if Account and User are in different databases. If they’re different tables in the same database, you can pass your code block into Account.transaction or User.transaction, whichever makes more sense.

At first, nested transactions look like they solve the multiple-resource distributed problem — if either block fails, then both are rolled back. However, your tables will be inconsistent if Account.transaction fails after User.transaction succeeds. It’s quite difficult to properly fix this problem. So, when using nested transactions, ensure that your data model can gracefully handle the case where the inner transaction succeeds but the outer transaction fails.

See also

Section 19.4 (pp 384-391) of Agile Web Development with Rails. Note that the passing of parameters to a transaction method (aka object transactions) as outlined on p.385 (second ed.) has been deprecated in Rails 2.0. See Rails Changeset 6439

Testing issues

Unit tests for transactional behavior is complicated when using transactional_fixtures in the TestCase. Because transactions can’t be nested, the tests can fail even when the transaction code is correct. See this bug for more details: http://dev.rubyonrails.org/ticket/5457.

Ruby Documentation