改ざん検知用hash chainを直列化するadvisory lockはcommitまで保持される。ads UPDATEの直後に監査INSERTを挟む構成だと、別トランザクションの行ロックと交差した瞬間に40P01デッドロックが起きる。直し方は監査書き込みを全UPDATEの後ろへ1本化すること。
改ざん検知用hash chainを1本に直列化するadvisory lockは、トランザクションのcommitまで保持される。 audit_logのBEFORE INSERTトリガ(migration 0003)は、並行INSERTの競合を避けるためpg_advisory_xact_lock(hashtext('audit_log')::bigint)を取得し、そのトランザクションが終わるまで離さない設計になっている。一方、portal配信の反映処理(applyPartnerDelivery)は、配信対象から外れた・戻った広告の監査記録を「ads UPDATE → 監査INSERT(advisory lock取得)→ 次のplacementのads UPDATE」という順で、placementごとに複数回挟み込んでいた。この処理が、運営画面の広告一時停止操作(これも「ads UPDATE → 監査INSERT」という同型の処理)と並行して走り、互いが先に更新した行を後から要求し合う順序になると、advisory lockを取得した側が相手の行ロックを待ち、相手はadvisory lockの解放を待つという循環ができ、PostgreSQLが40P01(deadlock detected)を検出して一方を強制終了させていた。直し方は、監査INSERTの呼び出しを対象の行UPDATEがすべて終わった後ろへ1本に集約すること。
手順の詳しい説明・確かめ方・よくある質問は、元の記事にまとめています。
→ audit_logのadvisory lockとUPDATE順序が作る新しいデッドロック窓|Rebounder Tech Blog