fix: resolve notification handling and maintain after_action for cycle regeneration
This commit is contained in:
parent
6a91f7c711
commit
f7c33bfc7d
3 changed files with 108 additions and 57 deletions
|
|
@ -38,10 +38,10 @@ defmodule Mv.MembershipFees.CycleGenerator do
|
|||
|
||||
"""
|
||||
|
||||
alias Mv.MembershipFees.CalendarCycles
|
||||
alias Mv.MembershipFees.MembershipFeeCycle
|
||||
alias Mv.MembershipFees.Changes.SetMembershipFeeStartDate
|
||||
alias Mv.Membership.Member
|
||||
alias Mv.MembershipFees.CalendarCycles
|
||||
alias Mv.MembershipFees.Changes.SetMembershipFeeStartDate
|
||||
alias Mv.MembershipFees.MembershipFeeCycle
|
||||
alias Mv.Repo
|
||||
|
||||
require Ash.Query
|
||||
|
|
@ -84,12 +84,20 @@ defmodule Mv.MembershipFees.CycleGenerator do
|
|||
|
||||
def generate_cycles_for_member(%Member{} = member, opts) do
|
||||
today = Keyword.get(opts, :today, Date.utc_today())
|
||||
skip_lock? = Keyword.get(opts, :skip_lock?, false)
|
||||
|
||||
# Use advisory lock to prevent concurrent generation
|
||||
# Notifications are handled inside with_advisory_lock after transaction commits
|
||||
with_advisory_lock(member.id, fn ->
|
||||
if skip_lock? do
|
||||
# Lock already set by caller (e.g., regenerate_cycles_on_type_change)
|
||||
# Just generate cycles without additional locking
|
||||
# When in transaction, notifications are returned and must be sent after commit
|
||||
do_generate_cycles(member, today)
|
||||
end)
|
||||
else
|
||||
# Use advisory lock to prevent concurrent generation
|
||||
# Notifications are handled inside with_advisory_lock after transaction commits
|
||||
with_advisory_lock(member.id, fn ->
|
||||
do_generate_cycles(member, today)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
|
@ -198,6 +206,7 @@ defmodule Mv.MembershipFees.CycleGenerator do
|
|||
|
||||
# Already in transaction: use advisory lock directly without starting new transaction
|
||||
# This prevents nested transactions which can cause deadlocks
|
||||
# Returns {:ok, cycles, notifications} where notifications should be sent after commit
|
||||
defp with_advisory_lock_in_transaction(lock_key, fun) do
|
||||
Ecto.Adapters.SQL.query!(Repo, "SELECT pg_advisory_xact_lock($1)", [lock_key])
|
||||
normalize_fun_result(fun.())
|
||||
|
|
@ -216,16 +225,16 @@ defmodule Mv.MembershipFees.CycleGenerator do
|
|||
end
|
||||
|
||||
# Execute function within transaction and return normalized result
|
||||
# When in transaction, create_cycles returns {:ok, cycles, notifications}
|
||||
# When not in transaction, create_cycles returns {:ok, cycles}
|
||||
# execute_within_transaction is always called within a Repo.transaction
|
||||
# create_cycles returns {:ok, cycles, notifications} when in transaction
|
||||
defp execute_within_transaction(fun) do
|
||||
case fun.() do
|
||||
{:ok, result, notifications} when is_list(notifications) ->
|
||||
# In transaction case: return result and notifications separately
|
||||
# Return result and notifications separately
|
||||
{result, notifications}
|
||||
|
||||
{:ok, result} ->
|
||||
# Not in transaction case: notifications handled by Ash automatically
|
||||
# Fallback case: no notifications returned
|
||||
{result, []}
|
||||
|
||||
{:error, reason} ->
|
||||
|
|
@ -234,15 +243,15 @@ defmodule Mv.MembershipFees.CycleGenerator do
|
|||
end
|
||||
|
||||
# Normalize function result to consistent format
|
||||
# When in transaction, create_cycles returns {:ok, cycles, notifications}
|
||||
# When not in transaction, create_cycles returns {:ok, cycles}
|
||||
defp normalize_fun_result({:ok, result, _notifications}) do
|
||||
# In transaction case: notifications will be sent after outer transaction commits
|
||||
# normalize_fun_result is called when already in a transaction (skip_lock? case)
|
||||
# create_cycles returns {:ok, cycles, notifications} when in transaction
|
||||
defp normalize_fun_result({:ok, result, notifications}) when is_list(notifications) do
|
||||
# Notifications will be sent after outer transaction commits
|
||||
# Return in same format as non-transaction case for consistency
|
||||
{:ok, result}
|
||||
{:ok, result, notifications}
|
||||
end
|
||||
|
||||
defp normalize_fun_result({:ok, result}), do: {:ok, result}
|
||||
defp normalize_fun_result({:ok, result}), do: {:ok, result, []}
|
||||
defp normalize_fun_result({:error, reason}), do: {:error, reason}
|
||||
|
||||
# Handle transaction result and send notifications if needed
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue