分散トランザクションの2相コミット:準備・決定・ブロッキング
2PCの準備と全体決定、調整者障害時に参加者が待つ理由、長期間のprepared状態の運用リスクを説明します。
発行・訂正窓口:infostoa · 編集基準
この記事の個別の執筆者・専門査読者の情報は提供されていません。
分散トランザクションでは、一つの業務が複数のデータベースにまたがると、一部だけ確定して注文と決済が異なる状態になることがあります。2相コミット(2PC)は、参加者にまずコミット準備を求め、その後、調整者が全体の決定を伝えるプロトコルです。原子性を支えますが、障害時に復旧状態の管理が必要で、作業が待機することもあります。
原子的コミットが必要な状況
分散トランザクションは、独立した資源への変更を一つの業務として整合させるときに使われます。注文に必要な更新を別々の保存先が担当し、一方だけが確定するとデータの不整合を直しにくくなる場合があります。2PCはすべての参加者が実行可能と答えた場合に全体をコミットし、準備できない参加者がいれば中止を指示します。これは決定を調整するプロトコルであり、あらゆる業務操作を元に戻せる保証ではありません。
準備段階で参加者が約束すること
調整者は各参加者に変更をコミットできるか尋ねます。参加者は準備状態と、再起動後にも処理を継続できる永続的な回復情報を記録してから、賛成または拒否を返します。Martin Fowlerのパターン説明では、必要なロックなどの資源を準備時に確保します。賛成票は最終コミットではなく、全体決定がコミットなら実行するという約束です。
全体のコミットと中止の決定
すべての参加者が賛成すれば、調整者はコミットを伝え、各参加者が変更を確定します。拒否が一つでもあれば中止を伝えます。PostgreSQLではPREPARE TRANSACTIONにより準備済み状態が元のセッションから切り離され、後でCOMMIT PREPAREDまたはROLLBACK PREPAREDで解決されます。これは外部トランザクション管理者を支えるPostgreSQL固有の実装で、SQL全般に共通する命令群ではありません。
調整者障害とブロッキングの意味
参加者が準備状態を永続化した後に調整者が使えなくなると、全体の決定がコミットか中止か分からず、参加者は整合性を保つため準備状態やロックを保持して待つことがあります。これがブロッキングで、必ず永久的なデッドロックになるという意味ではありません。調整者の耐久ログ、復旧処理、再起動後の決定再送で回復できます。ただし決定者とネットワークの両方が不確かな場合、参加者だけで別の決定を安全に作ることはできません。
専門家の視点:保持資源と復旧を設計する
PostgreSQLは準備状態を長期間残すとロックを保持し、VACUUMによる領域回収を妨げ、極端な場合はトランザクションIDのwraparoundを防ぐためサーバー停止につながり得ると注意しています。運用では未解決トランザクションを追跡し、外部管理者が速やかに終了できる設計が必要です。複数サービスに補償処理を行うSagaが代替になることもありますが、補償は原子的なロールバックと同じではなく、業務上の意味を別途設計します。
実装レビューで確認すること
参加者の準備記録が障害後に復旧できるか、調整者の決定を再送しても結果が安全か、未解決状態に警告と復旧担当者があるかを確認します。PostgreSQLでは、外部管理者が準備状態を追跡し速やかに解決できる場合に機能を有効にするよう、公式文書が注意しています。一貫性と可用性のトレードオフは業務の要件や各資源の機能に左右されます。
復旧時は参加者ごとの永続ログと調整者の決定ログを照合し、準備・コミット・中止の状態を確かめます。再起動後に運用者が推測で決めてはいけません。PostgreSQLのpg_prepared_xactsビューを外部管理者の記録と照合すれば、欠落や長期間残る処理を見つける手掛かりになります。決定を再送した場合に各資源で二重適用を防げるかも確認します。
出典
- PostgreSQL 16, PREPARE TRANSACTION — 準備状態、解決命令、外部管理者の目的、未解決状態への注意。PostgreSQL固有の実装文書。
- Unmesh Joshi, Two-Phase Commit (2023) — 準備・コミット投票と永続的な参加者決定を説明する実務パターン。