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.

This example will only take money from David and give it to Mary if neither
withdrawal nor deposit raise an exception. Exceptions
will force a ROLLBACK that returns the database to the state before the transaction
began. Be aware, though, that the objects will not have their
instance data returned to their pre-transactional state.

Though the transaction
class method is called on some Active
Record class, the objects within the transaction
block need not all be instances of that class. This is because transactions
are per-database connection, not per-model.

In this example a balance record is transactionally saved even
though transaction
is called on the Account class:

Transactions are not distributed across database connections

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:

Both save and destroy come wrapped in a transaction
that ensures that whatever you do in validations or callbacks will happen
under its protected cover. So you can use validations to check for values
that the transaction
depends on or you can raise exceptions in the callbacks to rollback,
including after_* callbacks.

As a consequence changes to the database are not seen outside your
connection until the operation is complete. For example, if you try to
update the index of a search engine in after_save the indexer
won’t see the updated record. The after_commit
callback is the only one that is triggered once the update is committed.
See below.

Warning: one should not catch ActiveRecord::StatementInvalid
exceptions inside a transaction
block. ActiveRecord::StatementInvalid
exceptions indicate that an error occurred at the database level, for
example when a unique constraint is violated. On some database systems,
such as PostgreSQL, database errors inside a transaction
cause the entire transaction
to become unusable until it’s restarted from the beginning. Here is an
example which demonstrates the problem:

# Suppose that we have a Number model with a unique column called 'i'.Number.transactiondoNumber.create(:i=>0)begin# This will raise a unique constraint error...Number.create(:i=>0)rescueActiveRecord::StatementInvalid# ...which we ignore.end# On PostgreSQL, the transaction is now unusable. The following# statement will cause a PostgreSQL error, even though the unique# constraint is no longer violated:Number.create(:i=>1)# => "PGError: ERROR: current transaction is aborted, commands# ignored until end of transaction block"end

transaction
calls can be nested. By default, this makes all database statements in the
nested transaction
block become part of the parent transaction.
For example, the following behavior may be surprising:

creates both “Kotori” and “Nemu”. Reason is the ActiveRecord::Rollback
exception in the nested block does not issue a ROLLBACK. Since these
exceptions are captured in transaction
blocks, the parent block does not see it and the real transaction
is committed.

In order to get a ROLLBACK for the nested transaction
you may ask for a real sub-transaction by passing :requires_new =>
true. If anything goes wrong, the database rolls back to the beginning
of the sub-transaction without rolling back the parent transaction.
If we add it to the previous example:

only “Kotori” is created. (This works on MySQL and PostgreSQL, but not
on SQLite3.)

Most databases don’t support true nested transactions. At the time of
writing, the only database that we’re aware of that supports true nested
transactions, is MS-SQL. Because of this, Active Record emulates nested
transactions by using savepoints on MySQL and PostgreSQL. See http://dev.mysql.com/doc/refman/5.0/en/savepoint.html
for more information about savepoints.

These callbacks are useful for interacting with other systems since you
will be guaranteed that the callback is only executed when the database is
in a permanent state. For example, after_commit
is a good spot to put in a hook to clearing a cache since clearing it from
within a transaction
could trigger the cache to be regenerated before the database is updated.

Caveats

If you’re on MySQL, then do not use DDL operations in nested transactions
blocks that are emulated with savepoints. That is, do not execute
statements like ‘CREATE TABLE’ inside such blocks. This is because
MySQL automatically releases all savepoints upon executing a DDL operation.
When transaction
is finished and tries to release the savepoint it created earlier, a
database error will occur because the savepoint has already been
automatically released. The following example demonstrates the problem:

foo was not permanently stored in the database, but it was transiently
saved, and this is reflected in the ActiveRecord model still in memory. But if
you try

foo.reload# raises ActiveRecord::RecordNotFound

Don’t let stale data confuse you after using transactions!

Edited to add: This particular example does not succeed in reproducing the
issue I encountered, which involved a slightly more complicated set of
nested transactions. I haven’t managed to produce a simple test case
where stale data remains in the model, but I have definitely experienced it
in my app.