Detect failure in action taken on an enumerable

I’m persisting an enumerable for which a database transaction function is called on each element. The persistence function nicely returns {:ok | :error, any}, but I’m unclear about how I should handle this response at the level of the enumerable. If I have five elements, and any one of them fails, I want to be able to take some action. Is there a more idiomatic way of doing this than just iterating through the responses?

def load_enumerable(enum) do
  enum
  |> Enum.map(&load_element(&1))
  |> Enum.find(fn {k, _v} -> k == :error end)
end

def load_element(element) do
  Ecto.Multi.new()
  |> Ecto.Multi.run(...)
  |> Ecto.Multi.run(...)
  |> Repo.transaction()
end

Anybody have a different approach to this?

1 Like

Maybe you can use a stream for that?

def load_enumerable(enum) do
  enum
  |> Stream.map(&load_element(&1))
  |> Enum.find(fn {k, _v} -> k == :error end)
end


iex(1)> test_enum = fn ->
...(1)>   1..1_000_000 |> Enum.map(fn i -> rem(i, 2) == 0 && {:ok, nil} || {:error, nil} end) |> Enum.find(fn {k, _} -> k == :error end)
...(1)> end
#Function<20.52032458/0 in :erl_eval.expr/5>
iex(2)> test_stream = fn ->
...(2)>   1..1_000_000 |> Stream.map(fn i -> rem(i, 2) == 0 && {:ok, nil} || {:error, nil} end) |> Enum.find(fn {k, _} -> k == :error end)
...(2)> end
#Function<20.52032458/0 in :erl_eval.expr/5>
iex(3)> :timer.tc test_stream
{313, {:error, nil}}
iex(4)> :timer.tc test_enum
{8263623, {:error, nil}}
2 Likes

This is definitely a more efficient approach! I’m just curious if there was another approach altogether that I am missing.

I would think that anyone persisting large batches of data to a database would want a list of exceptions that need attention. Perhaps this is the way to go.

1 Like