同時実行と競合制御
1件ずつなら正しい処理も、同時に動くと壊れます。時系列を分解し、lock・unique constraint・isolation・retryを組み合わせる方法を学びます。
境界、transaction、並行制御、脅威、テスト、障害対応をFailure Firstで学ぶ。
先に学ぶ内容 — Prerequisite: 基礎 / 銀行システム学ぶこと — LEARNING GOALS
このページで学ぶこと
- 代表的な競合パターン
- lock順序とdeadlock
- 再試行可能なtransaction設計
体験31 · CONCURRENCY CONTROL(競合を制御する)
残高を確認してから振込を実行するまでの間に、何も起きないと言えるか確かめよう
まずは予想をひと言。本文を読んだ後、教材内の参照元で実際のlock順序を確認します。
- 01まず予想する — PREDICT
まずここだけ:思ったことをひと言書く
残高を確認してから振込を実行するまでの間に、別の振込が同じ口座を操作することはないと思いますか?
正解でなくて大丈夫です。「〜だと思う」から始めてみましょう。
ヒント1を見る
『確認』と『実行』が別の瞬間なら、その間に何が起きうるでしょうか?
ヒント2を見る
2つの振込が同じ口座を同時に操作したら、何を先に決める必要があるでしょうか?
もっと詳しく:この問いで使う専門用語
- Interleavingは、複数処理の命令が交互に実行される順番です。
- TOCTOUは、確認した時点と実行時点の間に状態が変わる競合です。
- Deterministic lock orderは、全処理が同じ順番でresourceをlockする規則です。
日常語で理解する — PLAIN LANGUAGE
まず、ここだけつかむ
同時実行問題は、処理AとBの命令を交互に並べると見つけやすくなります。
lock・unique constraint・isolationは役割が異なり、1つで全競合を防げません。
deadlockやserialization failureは起こり得る前提で、安全にretryできるtransactionを作ります。
順番に理解する — DIFFICULT PARTS, STEP BY STEP
難しいポイントを、順番にほどく
専門用語を覚えることより、「なぜ必要か」「このシステムのどこで使うか」を理解することが目標です。
Interleaving
複数処理の命令が交互に実行される順番です。
単体では正しい処理が、並び方で壊れることを示せます。
2件の振込が同じ残高を読む時系列を書きます。
TOCTOU
確認した時点と実行時点の間に状態が変わる競合です。
認可済みと思ったactorが実行前に無効化される場合があります。
caller operatorをlockしactive状態を同じtransactionで確認します。
Deterministic lock order
全処理が同じ順番でresourceをlockする規則です。
循環待ちdeadlockを減らします。
2口座をUUIDの決定順でlockします。
発展 — ADVANCEDもっと詳しく確認する最初の学習では読まなくても問題ありません。仕組みをもっと深く理解したい場合に開いてください。
壊れるinterleavingを先に書く
| 問題 | 時系列 | 結果 |
|---|---|---|
| Lost update | AとBが残高100を読み、各80を引く | 両方成功または片方を上書き |
| Write skew | 別々の行を見て全体条件を判断 | 各行は正しいが全体不変条件を破る |
| Double execution | 同じrequestを同時に受付 | 業務recordと仕訳が2件 |
| Deadlock | T1が口座A→B、T2がB→Aの順でlock | 互いに相手のlockを待つ |
| TOCTOU | 権限確認後、実行前にoperator状態が変化 | 無効化後の権限で操作 |
制御方法は役割が違う
| 手段 | 守るもの | 限界 |
|---|---|---|
| Row lock | 読み取りから更新まで対象行を直列化 | 範囲と順序を誤るとdeadlock・待ち |
| Unique constraint | 同じidentityの重複recordを禁止 | 異なるkeyの意味的重複は止めない |
| Serializable | 直列実行と矛盾する結果をabort | retry設計が必要で負荷も増える |
| Advisory lock | 行に直結しない論理resourceを直列化 | key設計と解放・観測が難しい |
| Optimistic version | 更新対象が読んだ後に変化したか検知 | 衝突時の再読込とUXが必要 |
lock順序をglobal ruleにする
- 01transactionが触るresource集合を確定
- 02UUIDなど決定的な順にsort
- 03全write pathが同じ順でlock
- 04lock後に認可・状態・残高を再確認
- 05短いtransactionで更新
- 06deadlock/serialization errorを安全にretry
並行testの設計
- 十分な残高を同時に使う2件をbarrierで開始
- 正反対方向の振込を繰り返しdeadlockを観測
- 同じidempotency keyと同内容を同時送信
- 同じkeyと異内容を同時送信
- admin無効化と管理操作を同時実行
- 最終record数、journal数、残高合計、auditを検証
実際のMinibankでは
振込は2口座を決定順でlockし、lock後の残高を使います。管理操作も対象だけでなくcaller operatorのactive状態が競合しないよう、確認位置を読む必要があります。
並行testは単なる高速loopではなく、競合させたい読取・更新地点を揃え、最終不変条件で判定します。
体験学習の続き · LEARNING LOOP
ここまで読めましたか?続けて確認してみましょう
Interleaving・TOCTOUの考え方を読んだところで、教材内の参照元で実際のlock順序を確認し、自分の言葉で説明してみます。
- 02仕組みを確かめる — CONCEPT CHECK
画面操作なしで、仕組みを確かめる
この仕組みは、Mini Bankの画面には現れません。次のEvidenceとCode Trail、教材本文だけで理解を進めます。
- 03証拠を確認する — EVIDENCE
何を見たか、1つずつ確かめる
Evidence(判断の根拠となる記録)を確認したらチェックします。各項目の「何を見るか」を手がかりにしてください。
画面を開けない項目は、Source Trail(教材の参照元)と教材の説明で確認できます。失敗を実際に起こす必要はありません。
- 04コードの処理経路を追う — CODE TRAIL
コードを書く必要はありません
ここでは「説明されている仕組みが、実際のMini Bankのどこに実装されているか」を確認します。まずは日本語の役割説明を読み、入力した依頼がどこへ渡るかをつかみましょう。コードの全文を読む必要はありません。
まずadmin操作のTOCTOU対策を見て、次に振込の口座lock順序を見ます。同時実行の何を守っているかを確かめましょう。
教材の参照元を確認する — Source Trail ↓admin callerのTOCTOU対策。lock後の状態再確認を探します。
supabase/migrations/20260823100001_fix_admin_toctou_lock_own_operator_row.sql振込のlockとidempotency。決定順でのlock取得を探します。
supabase/migrations/20260823170001_create_transfer_beneficiary_and_otp.sql - 05自分の言葉で説明する — EXPLAIN
LEVEL 1 — やさしい説明(まずここだけ)
なぜ2つの口座を『常に同じ順番』でlockする必要があるのでしょうか? 短い2文を目安に説明してください。
日常語だけで完了できます。専門用語の数や正誤を自動採点するものではありません。
ヒント1を見る
「決定順」と「lock」という2つの言葉を使って書いてみましょう。
ヒント2を見る
なぜ全処理が同じ順番でlockする必要があるのか考えてみましょう。
もっと詳しく:LEVEL 2 — 専門用語を使った説明(任意)
Interleaving、TOCTOU、Deterministic lock orderの3つを使って、競合が起きる理由と対策を説明してみましょう。
挑戦する場合は、上の説明欄に追記できます。追加しなくても完了できます。
説明の確認ポイント — Checkpoint- 確認と実行の間に状態が変わりうると説明できる
- 決定順のlockがdeadlockを減らすと説明できる
理解を確かめる — CHECK YOUR UNDERSTANDING
3問で理解チェック
答えを頭の中で考えてから、各項目を開いて確認してください。
Q1残高を確認してからUPDATEすれば安全ですか?
確認と更新の間に他処理が動くため、lock後の正式残高で再確認する必要があります。
Q2deadlockを完全になくせますか?
共通lock順で減らせますが、依存経路全体では起こり得るためabort後の安全なretryも設計します。
Q3並行testはloopを高速化すればよいですか?
競合地点をbarrierで揃え、最終不変条件・record数・残高・auditで判定する必要があります。
完全に暗記する必要はありません。大切な考え方を説明できそうなら押してください。
教材の参照元 — SOURCE TRAIL
根拠と関連教材
CURRENT IMPLEMENTATIONは確認したrepository内の実装です。HISTORICAL IMPLEMENTATIONは導入・修正の履歴で、後続migrationによる再定義があり得ます。RELATED LESSONは教材への案内で、実装の一次Sourceではありません。ASSUMPTIONは仮定、UNKNOWNは未確認を表します。
確認基点: d02eae8914f39cbf06bfe422dc39c71886f82b3e / 2026-09-11。稼働環境への適用確認: UNKNOWN。パス実在の自動検査と、記述した技術的事実の確認は別です。
- 過去の実装 — HISTORICAL IMPLEMENTATION
supabase/migrations/20260823100001_fix_admin_toctou_lock_own_operator_row.sqladmin callerのTOCTOU対策 - 過去の実装 — HISTORICAL IMPLEMENTATION
supabase/migrations/20260823170001_create_transfer_beneficiary_and_otp.sql振込のlockとidempotency - 現在の実装 — CURRENT IMPLEMENTATION
scripts/regression/concurrency回帰test