feat: improve error handling in CycleGenerator

- Handle Task crashes in async_stream with {:exit, reason}
- Return {:error, {:partial_failure, successes, errors}} when some cycles fail
- Previously returned {:ok, successful} even on partial failures
- Improves debuggability and allows callers to handle partial failures
This commit is contained in:
Moritz 2025-12-12 16:23:12 +01:00
parent a99f56969d
commit cf8a1fa30d

View file

@ -142,7 +142,15 @@ defmodule Mv.MembershipFees.CycleGenerator do
|> Task.async_stream(fn member ->
{member.id, generate_cycles_for_member(member, today: today)}
end)
|> Enum.map(fn {:ok, result} -> result end)
|> Enum.map(fn
{:ok, result} ->
result
{:exit, reason} ->
# Task crashed - log and return error tuple
Logger.error("Task crashed during cycle generation: #{inspect(reason)}")
{nil, {:error, {:task_exit, reason}}}
end)
end
defp build_results_summary(results) do
@ -298,7 +306,7 @@ defmodule Mv.MembershipFees.CycleGenerator do
end
defp create_cycles(cycle_starts, member_id, fee_type_id, amount) do
cycles =
results =
Enum.map(cycle_starts, fn cycle_start ->
attrs = %{
cycle_start: cycle_start,
@ -314,15 +322,16 @@ defmodule Mv.MembershipFees.CycleGenerator do
end
end)
errors = Enum.filter(cycles, &match?({:error, _}, &1))
{successes, errors} = Enum.split_with(results, &match?({:ok, _}, &1))
successful_cycles = Enum.map(successes, fn {:ok, cycle} -> cycle end)
if Enum.empty?(errors) do
{:ok, Enum.map(cycles, fn {:ok, cycle} -> cycle end)}
{:ok, successful_cycles}
else
Logger.warning("Some cycles failed to create: #{inspect(errors)}")
# Return successfully created cycles anyway
successful = Enum.filter(cycles, &match?({:ok, _}, &1)) |> Enum.map(fn {:ok, c} -> c end)
{:ok, successful}
# Return partial failure with both successful and failed cycles
# This allows callers to decide how to handle partial failures
{:error, {:partial_failure, successful_cycles, errors}}
end
end
end