Concurrent create_member transactions took FK FOR KEY SHARE (MultiXact) locks on shared rows across members/users/membership_fee_types and could form a cross-transaction cycle, producing intermittent PostgreSQL deadlocks (40P01) under load. Making the three foreign keys DEFERRABLE INITIALLY DEFERRED moves the check to commit time and breaks the cycle, without weakening integrity (NOT NULL and ON DELETE RESTRICT are unaffected).
32 lines
1.3 KiB
Elixir
32 lines
1.3 KiB
Elixir
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
|