Nested transactions with Rails
In Ruby on Rails, nested transactions are owned by the most outer transaction. It means that raising ActiveRecord::Rollback
error in the code below, will not prevent it from creating 2 users in the database:
User.transaction do
User.create(username: 'Kotori')
User.transaction do
User.create(username: 'Nemu')
raise ActiveRecord::Rollback
end
end
It happens because exceptions captured within second transaction blocks are not visible to the first transaction. To make a nested transaction to behave like you would normally expect, you need to pass requires_new: true
parameter:
User.transaction do
User.create(username: 'Kotori')
User.transaction(requires_new: true) do
User.create(username: 'Nemu')
raise ActiveRecord::Rollback
end
end
ActiveRecord
emulates nested transactions via savepoints (supported by MySQL and PostgreSQL). More info here.
Savepoint vs nested transaction
On the first sight savepoint might look identical to a nested transaction, but there is a main difference between them: rolling back outer transaction will revert changes from all nested savepoints, meanwhile true nested transaction could still be committed.