omin
Question about ecto transaction, multiple associations, and rollbacks
I have a transaction that updates upto 3 tables and it became long and ugly. I’d love to learn more to be able to write cleaner and more efficient code.
I have a form that takes inputs to update upto 3 tables.
a. Uses external API to cache the data if it expired or doesn’t already exist.
b. build_assoc with (a) for a new data point
c. build_assoc with (a) and (b) for a new data point
Some specific questions:
- Since
transactionsshould be kept as short as possible, would you recommend fetching from the external API outside thetransaction? (This is what I’ve done but it adds complexity to the code.) - What would be to best practice to manage layers of associations? I found http://stackoverflow.com/questions/38033817/how-to-make-forms-and-transactions-play-well-in-phoenix-ecto/39415888#39415888 and I’m leaning toward the pattern matching example.
- How would you rollback multiple changesets?
Most Liked
josevalim
It seems Ecto.Multi may be exactly what you need: Ecto.Multi — Ecto v3.14.0
It answers questions 1 and 3 and it may as well answer question 2 too.
dimitarvp
Almost two years later: yes, according to the official docs any exception raised inside the function given to Repo.transaction will result in a rollback.
Please note though, the third insert in your code is not using the bang variant of the function (namely it’s not insert!) and will thus not raise an exception so the transaction will likely still succeed with only partial success – not good.
EDIT: The above is NOT true: Ecto.Multi docs
To use Repo.transaction, do one of these:
- Either use bang functions everywhere in the transaction function (
Repo.insert!,Repo.update!etc.), or… - Use the
withkeyword and chain all the operations through non-bang functions and callRepo.rollbackin theelseclause – which would mean that the first failed operation will return{:error, reason}and the transaction will return that exact error so you can troubleshoot further afterwards. Or… - Just use
Ecto.Multiwhich will give you even more info if an operation fails. It’s really the best way of doing such composite operations ever since it was introduced. Just have oneEcto.Multivariable and append all your operations to it, then just execute it at once:Repo.transaction(your_multi).
It’s best if you don’t mix these styles. Just pick one and stick with it.
I started off using more hacky solutions and trying to be clever but nowadays I am always using Ecto.Multi and I am very pleased with the results. The code is much easier for a human to understand as well, which is a huge bonus win.
omin
Thanks for the reply @jose.
Do you think we can go through an example? I couldn’t find anything online ![]()
transaction = Repo.transaction fn ->
location = location || Repo.insert!(location_changeset)
item_name = photo_params["item_name"]
item = Repo.get_by(App.Item, [name: item_name, location_id: location.id])
item_changeset =
location
|> build_assoc(:items)
|> App.Item.changeset(Map.put(photo_params, "name", item_name))
item = item || Repo.insert!(item_changeset)
photo_changeset =
location
|> build_assoc(:photos, item_id: item.id, user_id: current_user.id)
|> Photo.changeset(photo_params)
Repo.insert(photo_changeset)
end
case transaction do
{:ok, _photo} ->
conn
|> redirect(to: phto_path(conn, :index))
{:error, changeset} ->
render(conn, "new.html", changeset: changeset)
end
Would ecto rollback everything when the second or third Repo.insert fails? Also, is there a way to cascade a changeset with multiple schemas?
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








