madili

madili

why transaction/2 raises an exception instead of a return tuple

Hi,
Looking at the documentation of the transaction/2 function it should return a {:ok, result} or {:error, reason} tuple. However, if I use insert_all/3 with transaction/2 I don’t get the expected result, but an exception is thrown anyway.

For example: if I try to insert a duplicate record an exception is thrown instead of returning a tuple with {:error, reason}.

With function:

case Repo.transaction(fn -> Repo.insert_all(SomeSchema, inserts) end) do
	{:ok, value} -> :ok
	{:error, reason} -> :error
end

With Ecto.Multi:

Multi.new()
|> Multi.insert_all("insert", SomeSchema, inserts)
|> Repo.transaction()
|> case do
  {:ok, _} -> :ok
  {:error, _, _, _} -> :error
end

I know that insert_all/3 has an option (on_conflict: :nothing) to prevent an exception from being raised. Even knowing this, it is a surprise that the code works in an “unsafe” way, why does it work like this?

Investigating the code of the db_connection lib, I noticed that there is a transaction function called by postgrex, and there is a version that runs the code inside a try/catch: db_connection/lib/db_connection.ex at v2.4.1 · elixir-ecto/db_connection · GitHub, so I was assuming that the function was safe.

I appreciate anyone who can help me.

Most Liked

jeremyjh

jeremyjh

I think its a valid question. There is a convention in the Elixir standard library that functions which raise exceptions end with a bang, (such as Map.fetch!) and often have a corollary that returns an error tuple.

The difference here, is that the exception is not coming from Repo.transaction. It’s coming from the code you are running inside the thunk; that is your code and your exception. Catching that and turning it into a tuple would be much more surprising to me than raising it.

Maybe the question is really about Repo.insert_all. This function will raise exceptions for all kinds of underlying database conditions including constraint violations, read-only databases, missing tables etc. The conflict behaviour is well-documented in the function docs and is consistent with other Ecto operations. The other exceptions are … well…exceptional. Those are almost always programmer errors and there is no sensible way to handle them other than to crash in my opinion.

josevalim

josevalim

Creator of Elixir

Non-bang functions never means it will never raise. For example, pass an integer to File.read/1. They will return tagged tuples for some class of errors that are up to the library to decide.

Plus transaction/2 is running your code via a function, so we shouldn’t compare it with File.read/1. A more apt comparison here would be File.open/2, which also doesn’t handle your own exceptions inside the function either.

al2o3cr

al2o3cr

That code re-raises everything but the specific :throw on lines 866-868.

Repo.transaction rolls back when an exception occurs but does not stop them. From the docs:

If an unhandled error occurs the transaction will be rolled back and the error will bubble up from the transaction function. If no error occurred the transaction will be committed when the function returns. A transaction can be explicitly rolled back by calling rollback/1, this will immediately leave the function and return the value given to rollback as {:error, value}.

Where Next?

Popular in Questions Top

New
Tee
can someone please explain to me how Enum.reduce works with maps
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New

Other popular topics Top

9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
Qqwy
Update: How to use the Blogs & Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3271 126479 1222
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement