Handle Transactions

This guide shows you how to use database transactions for atomic operations.

Basic Transaction

Wrap operations in a transaction block:

AcmeDB.transaction do
  user = User.create!(name: "Alice", email: "alice@example.com")
  Profile.create!(user_id: user.id, bio: "Hello!")
  Account.create!(user_id: user.id, balance: 0.0)
end

If any operation fails, all changes are rolled back.

Transaction with Return Value

user = AcmeDB.transaction do
  user = User.create!(name: "Alice", email: "alice@example.com")
  Profile.create!(user_id: user.id)
  user  # Return the user
end

puts user.id  # Use the created user

Manual Rollback

Explicitly rollback a transaction:

Error Handling

Handle transaction errors:

Nested Transactions (Savepoints)

Use savepoints for nested operations:

Transaction Isolation Levels

Set isolation level for a transaction:

Locking Records

Pessimistic Locking

Lock records for update:

Select for Update

Transfer Example

Complete money transfer with transactions:

Batch Operations

Process batches within transactions:

Transaction Callbacks

Run code after transaction commits:

Read-Only Transactions

For read-heavy operations:

Best Practices

  1. Keep transactions short - Long transactions hold locks

  2. Order lock acquisition - Always lock in same order to prevent deadlocks

  3. Handle failures - Always rescue and handle transaction failures

  4. Don't mix concerns - Avoid external API calls inside transactions

  5. Use appropriate isolation - Higher isolation = lower concurrency

See Also

Last updated

Was this helpful?