Ecto nested transactions - not clear

ecto

#1

Hello,

the docs for Ecto.Repo.transaction/2 read:

If transaction/2 is called inside another transaction, the function is simply executed, without wrapping the new transaction call in any way. If there is an error in the inner transaction and the error is rescued, or the inner transaction is rolled back, the whole outer transaction is marked as tainted, guaranteeing nothing will be committed.

However, the What’s new in Ecto ebook states that:

(…) The snippet above starts a transaction and then calls transfer_money/3 that also runs in a transaction. This works because Ecto converts nested transaction into savepoints. In case an inner transaction fails, it rolls back to its specific savepoint.

Which one is true?

It’s not clear to me what’s the behaviour of nested transactions, and what happens to the parent transaction when a nested one fails.

Thanks!


#2

:wave:

Given that those docs were written 3 years ago I’d bet that the what’s new in ecto book is right - good catch!

However, I can’t find it - if I didn’t take a wrong turn then transaction eventually ends up in DBConnection.transaction which in case of a nested transaction calls DBConnection.transaction_nested which doesn’t seem to make use of savepoints unless I’m missing something.

I’m gonna @michalmuskala as he probably knows/can shed some light on this. I’m really interested in this, as nested transactions are quite tricky but much too my delight both options that are presented here are much better than what ActiveRecord does by default if I understand them correctly :slight_smile:


#3

The docs are correct here, we flatten all the transactions. Once anything fails in a transaction, the transaction is tainted and any subsequent operations will also fail - the only thing you can do is roll it back.

The book is incorrect here. @josevalim I think you have the power to correct it, right?


#4

Yup, will do, thanks. Although I am not quite sure when a new edition will be out.