【要約】PostgreSQLでデッドロックを意図的に再現する ― Pythonのthreadingで2セッションを同時実行 [Zenn_Python] | Summary by TechDistill
> Source: Zenn_Python
Execute Primary Source
// Problem
開発者がデッドロックの挙動を検証しようとする際、複数のセッションを正確に同期させて同時実行させる手法の確立に苦慮する。従来のシェルスクリプトを用いた手法では、以下の理由により再現が困難である。
- ・psqlの個別呼び出しでは、プロセス終了時にトランザクションが消失する。
- ・ヒアドキュメントでは、セッション間の実行タイミングを制御できない。
- ・名前付きパイプ(mkfifo)は、読み手不在時のブロック仕様により同時実行制御に向かない。
// Approach
Pythonのthreadingモジュールを活用し、スレッド間で実行タイミングを制御する手法を採用する。具体的には以下のステップでデッドロックを誘発させる。
- ・
threading.Eventを用いて、スレッド間で「ロック取得完了」を通知する同期ポイントを構築する。 - ・セッションAがid=1を、セッションBがid=2をロックした直後に、互いの保持するロックを要求させる。
- ・ロック取得順を昇順に統一することで、デッドロックが発生しないことをコードで検証する。
// Result
Pythonを用いた同期制御により、PostgreSQLがデッドロックを検知し、片方のセッションを自動的にロールバックする挙動を再現した。
- ・デッドロックの根本原因が「ロック取得順の逆転」であることを実証した。
- ・ロック順の統一、トランザクションの短縮、
SELECT FOR UPDATEの活用といった具体的な回避策を提示した。 - ・DDDのRepositoryパターンにおいて、ユースケース層で更新順序を制御すべきという設計指針を得た。
Senior Engineer Insight
> デッドロックは、単なるDBの挙動ではなく、アプリケーションの設計不備に起因する。特にDDDを採用する場合、Repositoryの呼び出し順序がユースケース層で制御されていないと、予期せぬ競合を招く。高並列なシステムでは、ロック順の昇順統一を徹底し、トランザクションの生存期間を最小化する設計が不可欠である。本記事の検証手法は、競合が発生しやすい複雑なビジネスロジックの設計検証において、非常に有用なアプローチである。