diff --git a/priv/repo/migrations/20260616165309_make_member_user_fks_deferrable.exs b/priv/repo/migrations/20260616165309_make_member_user_fks_deferrable.exs new file mode 100644 index 0000000..2fa45fb --- /dev/null +++ b/priv/repo/migrations/20260616165309_make_member_user_fks_deferrable.exs @@ -0,0 +1,32 @@ +defmodule Mv.Repo.Migrations.MakeMemberUserFksDeferrable do + @moduledoc """ + Makes the members/users foreign keys DEFERRABLE INITIALLY DEFERRED. + + Concurrent `create_member` transactions take FK `FOR KEY SHARE` (MultiXact) + locks on these foreign keys at statement time and can form a cross-transaction + lock cycle, producing a PostgreSQL `deadlock_detected` (40P01). Deferring the + FK checks to commit time breaks the cycle. + + Constraint deferrability is not tracked by AshPostgres resource snapshots, so + this is a hand-written migration with raw `execute/2`. Do not regenerate it + via `mix ash_postgres.generate_migrations`. + """ + use Ecto.Migration + + def change do + execute( + "ALTER TABLE users ALTER CONSTRAINT users_member_id_fkey DEFERRABLE INITIALLY DEFERRED", + "ALTER TABLE users ALTER CONSTRAINT users_member_id_fkey NOT DEFERRABLE" + ) + + execute( + "ALTER TABLE users ALTER CONSTRAINT users_role_id_fkey DEFERRABLE INITIALLY DEFERRED", + "ALTER TABLE users ALTER CONSTRAINT users_role_id_fkey NOT DEFERRABLE" + ) + + execute( + "ALTER TABLE members ALTER CONSTRAINT members_membership_fee_type_id_fkey DEFERRABLE INITIALLY DEFERRED", + "ALTER TABLE members ALTER CONSTRAINT members_membership_fee_type_id_fkey NOT DEFERRABLE" + ) + end +end