もくじ
- 第1章:「たまに固まる」が一番厄介:現場が抱える“説明できない停止”
- 第2章:EDEADLK(35)は悪い知らせではない:ハングする前に検出できたという意味
- 第3章:どこで返る?pthread mutex/futex/fcntlロックで起きる典型シナリオ
- 第4章:まずは再現できる形に落とす:最小コードとスレッドダンプで“因果”を作る
- 第5章:観測の基本セット:strace・perf・ftraceで「どのロックで止まったか」を掴む
- 第6章:カーネル側の手掛かり:/proc/locks・lockdep・ログから循環待ちを特定する
- 第7章:設計で勝つ:ロック順序・階層化・粒度調整で“循環”を作らない
- 第8章:実装で逃がす:trylock/timedlock/バックオフで進捗保証を入れる
- 第9章:テストで潰す:負荷・並行度・フォールト注入で「再発しない」を証明する
- 第10章:帰結:EDEADLKを“仕様”にする—「止まらない」より「必ず進む」設計へ
【注意】本記事は、Linuxにおける EDEADLK (35)(デッドロック回避エラー)を題材に、並行処理で起きやすい停止要因の“被害最小化(ダメージコントロール)”と検出・対策の考え方を整理した一般的な情報提供です。実際の最適解は、対象システムのスレッド設計、ロック種別、I/O構成、負荷、運用要件、復旧期限などで大きく変わります。重要データや基幹サービスが関わる場合は、判断を誤ると障害が長期化するため、株式会社情報工学研究所の様な専門事業者に相談してください。
「たまに固まる」が一番厄介:現場が抱える“説明できない停止”
「再現しない」「ログに何も残らない」「でもユーザーは困っている」——この3点セットが揃うと、現場は一気に消耗します。CPU使用率は高くないのに応答が返らない。コンテナは生きているように見えるのに、APIが沈黙する。監視は赤くならないのに、オンコールだけが鳴る。こういう“静かな停止”の代表格がデッドロックです。
心の会話で言うと、たぶんこうです。
「また固まった…でも落ちてもない。これ、どう説明すればいいんだ…」
「原因は分からないけど、とりあえず再起動で収束(クールダウン)させるしかないの、しんどい…」
デッドロックは“いつか必ず”起きるわけではありません。スレッドの実行タイミング、I/O待ちの揺らぎ、ロック取得順序の偶然の一致で、ある瞬間だけ成立します。だからこそ、障害対応では「再現できない」ことが多いのですが、ここで重要なのは、再現性が薄くても“観測できる形に落とす”ことです。
まず、現象を言葉で切り分けます。
- プロセスが落ちているのか(クラッシュ)
- 応答が遅いのか(性能劣化)
- 進捗が止まっているのか(ハング/デッドロック)
デッドロックは「進捗が止まる」問題です。ここを曖昧にすると、対策が“速くする”方向にずれていきます。必要なのは速度ではなく、進捗保証(必ず前に進む設計)です。この記事は、その方向に頭を切り替えるための“場を整える”ところから始めます。
そして、EDEADLK(35)は、その進捗停止を「検出できた」ケースの入口です。次章で、EDEADLKが返る意味と、返らない(=固まる)ケースとの差を整理します。
EDEADLK(35)は悪い知らせではない:ハングする前に検出できたという意味
EDEADLKは、Linuxでは errno 35 として知られる「Deadlock avoided(デッドロックを回避した)」のエラーです。ポイントは“回避した”という言葉で、完全に停止してから気づくのではなく、「このまま待つとデッドロックになる可能性が高い」または「同一スレッドが同じロックを再取得しようとして自分で詰む」状況を、システム側が検出して返してきた、という性質があります。
つまり、EDEADLKが返った時点で、すでに“固まる未来”に近づいていますが、まだ手が打てます。ここでの基本姿勢は、原因究明を先送りにしてでも、まず被害最小化(ダメージコントロール)を優先することです。
心の会話にすると、こう切り替えます。
「よし、固まる前に返ってきた。いまならログを残して、安全にリセットできる」
「“待ち続ける”のが一番まずい。タイムアウトか切り戻しで進捗を取り戻そう」
EDEADLKが返る代表的なパターンは大きく2つです。
- 自己デッドロック:同一スレッドが、非再帰(通常)のミューテックス等を二重にロックしようとして詰む
- 循環待ちの検出:ロック待ちの関係から、循環(AがB待ち、BがA待ち等)になり得ると判断される
ただし重要な注意点があります。すべてのデッドロックがEDEADLKとして検出されるわけではありません。多くのユーザー空間ロック(pthreadの通常ミューテックス等)は、誤った使い方をすると単に永久待ちになり、errnoとして返りません。だからこそ、EDEADLKは「検出できた貴重なシグナル」であり、握りつぶすべきではありません。
運用上の実務としては、EDEADLKを見たら「例外扱いで落とす」か「安全な再試行に切り替える」かを、設計で決めておくのが現実的です。曖昧にすると、現場はこうなります。
「ここ、エラー返るんだ…でも、とりあえずリトライでいい?いや、余計に詰まる?」
この迷いを減らすために、次章で「どのAPIでEDEADLKが返り得るのか」「返った時に何をログに残すべきか」を、具体例で整理します。
どこで返る?pthread mutex/futex/fcntlロックで起きる典型シナリオ
EDEADLKは「どこで返ったか」によって、意味合いと対策が変わります。現場で遭遇しやすい入口は、概ね次の3系統です。
- pthread系(ユーザー空間の排他):mutex/condなどの同期原語の使い方で自己デッドロックや不整合が露見する
- futex(pthreadの下支え):多くの場合は直接触らないが、strace等で手掛かりとして現れる
- fcntlのファイルロック(カーネルが関与する待ち):F_SETLKW等で待つ際に、デッドロック検出が働いてEDEADLKが返ることがある
代表例1:pthread_mutex_lockでの自己デッドロック(エラーチェックミューテックス)
同一スレッドが同じミューテックスを二度ロックするのは、非再帰ミューテックスでは典型的な自己デッドロックです。通常のミューテックスだと“固まるだけ”になりがちですが、エラーチェック属性(PTHREAD_MUTEX_ERRORCHECK)を使っている場合、ロックでEDEADLKが返ることがあります。これは「固まる前に検出できた」ケースで、ログを残して失敗として扱う設計が取りやすいのが利点です。
ここでの落とし穴は、「EDEADLKを握りつぶしてリトライ」してしまうことです。自己デッドロックは構造的なバグなので、リトライで直る保証はありません。むしろ“同じ道をもう一回歩く”だけになります。
代表例2:fcntl(F_SETLKW)の待ちでのデッドロック検出
ファイルロック(レコードロック)を待つAPI(例:fcntlのF_SETLKW)は、カーネル側で待ち関係を把握できるため、循環待ちの疑いがあるとEDEADLKで返すことがあります。アプリが複数ファイルや複数範囲のロックを“順不同”で取得し始めると、プロセス間で循環が成立しやすくなります。
この系統は「同じ順序でロックを取る」「ロック粒度を整理する」「そもそもロック対象を減らす」など、設計の話に直結します。EDEADLKはその設計上の歪みが表に出たサインと捉えるのが安全です。
どの入口かを一瞬で整理するための対応表
| 入口 | EDEADLKの典型的な意味 | 初動(被害最小化) |
|---|---|---|
| pthread mutex | 自己デッドロック検出、または同期設計の破綻の兆候 | スレッドID/ロック名/呼び出し元を記録し、処理を失敗として切り上げ(必要なら安全なリセット) |
| fcntl F_SETLKW | ファイルロック待ちの循環可能性をカーネルが検知 | ロック対象と順序をログ化し、取得順序の統一・粒度見直しを優先課題にする |
| strace上のfutex | 直接原因というより、どの同期が詰まっているかの手掛かり | 該当スレッドのバックトレース採取(pstack/gdb等)へつなげる |
ここまでで、EDEADLKを「単なるエラー」ではなく「固まる前の警告」として扱う準備ができました。次章では、再現しづらいデッドロックを“再現できる形に落とす”ために、最小コード化・スレッドダンプ・ログ設計をどう進めるかを掘り下げます。
まずは再現できる形に落とす:最小コードとスレッドダンプで“因果”を作る
デッドロック対応で一番つらいのは、「たぶんロックが原因」という感覚があっても、因果関係を説明できる材料が足りない状態です。ここで焦って“推測の修正”を始めると、現場はさらに混乱します。まずやるべきは、再現に近い条件を切り出し、観測可能な形にして、議論の温度を下げる(クールダウン)ことです。
心の会話としてはこうです。
「直した“気がする”は危険。再現・観測・証拠の順で、落ち着いて行こう」
「関係者に説明できる形を作らないと、また同じ夜を繰り返す」
最小コード化(MRE: Minimal Reproducible Example)でやること
本番コードをそのまま追いかけるのは時間がかかります。特にSRE/情シスの立場だと、アプリの内部実装に深く踏み込めないことも多い。そこで、再現性を上げるために「最小構成」を作ります。要点は“ロックの関係だけを残す”ことです。
- ロック取得の順序(A→B、B→Aなど)を固定して書き起こす
- ロックの粒度(1つの大きいロックか、複数の小さいロックか)を明示する
- スレッド数・ループ回数・待ち時間(sleep)でタイミングの揺らぎを増やす
実務的には、次のような観点で“本番の匂い”を残します。
- 本番で使っているロック種別(pthread mutex、rwlock、ファイルロック等)を揃える
- クリティカルセクション内のI/O(ログ出力、DB呼び出し、ファイル書き込み等)があるなら、ダミーでも残す
- 例外やエラー処理でロック解放が漏れるパスがあるなら、その分岐を残す
「本番の複雑さを全部持ってくる」のではなく、「ロック関係の構造だけ」を持ってくるのがコツです。これで、アプリチームにも“議論できる材料”を渡せます。
スレッドダンプ(バックトレース)で“止まった場所”を固定する
デッドロックの証拠として強いのは、止まっているスレッドのバックトレースです。特に「どのロックを待っているか」「誰がそのロックを保持しているか」が見えると、一気に因果が繋がります。一般論として、次の採取を検討します。
- gdb で対象プロセスに attach して、全スレッドの backtrace を採取
- pstack 相当のツールで簡易に全スレッドを出す(環境により利用可否は要確認)
- アプリが独自に“状態ダンプ”を吐けるなら、EDEADLK検出時に必ず出す
ここで注意したいのは、本番でgdb attachは運用リスクがあるという点です。停止や遅延を誘発する可能性がゼロではありません。だからこそ、事前に「緊急時の採取手順」「採取してよい時間帯」「ロールバック」を決めておくと、現場の心理的負担が減ります。これも“場を整える”一部です。
EDEADLKが返った瞬間に残すべきログ(最低限)
EDEADLKは“貴重な瞬間”です。逃すと、次は単なるハングになり、ログが残らないこともあります。最低限、次を揃えると調査が前に進みやすいです。
| ログ項目 | 理由 | 例 |
|---|---|---|
| スレッドID | 同一スレッドの自己デッドロックか、別スレッド循環かの切り分けに必須 | TID、pthread_self、OSスレッドID |
| ロック識別子 | どのロックが問題かを特定する軸になる | mutex名、アドレス、ファイル名+範囲 |
| 呼び出し元 | 同じロックでも取得箇所が複数あると原因がぶれる | 関数名、ファイル:行、スタック短縮 |
| 処理コンテキスト | API要求IDやジョブIDがないと再現条件が追えない | request_id、tenant、job_id |
これらを揃えると、「たまたま起きた」から「この順序でロックを取ったときに起きた」へ進めます。再現性が低い障害ほど、この一段上の説明が価値になります。
第4章の結論はシンプルです。デッドロックを“議論できる問題”に落とし、証拠を揃え、次章の観測(strace/perf/ftrace)につなげます。次は、実際に「どのロックで止まったか」を短時間で掴む基本セットを扱います。
観測の基本セット:strace・perf・ftraceで「どのロックで止まったか」を掴む
デッドロック調査は、推理小説ではなく計測の問題です。とはいえ、現場で使える時間は限られています。「全部入れ替える」「大改修する」ではなく、いま起きている停止を“観測できる形”にして、被害最小化(ダメージコントロール)と恒久対策の両方に繋げる——これが現実的なゴールになります。
心の会話はこうなります。
「機能追加より、まず観測。観測できないものは改善できない」
「“どのロックが詰まったか”さえ分かれば、修正は案外まっすぐ行ける」
strace:今どのsyscallで止まっているかを掴む
straceは、プロセスがどのシステムコールで待っているかを可視化します。ロックそのものはユーザー空間で完結する場合も多いですが、pthreadの内部ではfutexを通じてカーネルに待機を委ねることがあり、そこが手掛かりになります。典型的には、futex待ちが長時間続く、特定のスレッドだけが同じ待ちに留まり続ける、といった形で現れます。
ここで大事なのは、straceを見て「futexが悪い」と短絡しないことです。futexは“待ちの出口”であって、原因はロック順序や解放漏れにあります。straceは「止まっている場所」を示すコンパスだと捉えるのが適切です。
perf:CPUが動いていないなら、何を待っているのかを見る
デッドロックやハングは、CPUを使わないことが多い一方で、スピンロックや過剰なリトライでCPUを燃やす形の“疑似デッドロック”もあります。perfは、その切り分けに役立ちます。CPUサンプルが特定のロック関数や待機ループに偏っているなら、待ちが本当にブロックなのか(sleep/futex)それともスピンなのか(busy wait)を判断できます。
運用の実感としては、「CPUが静かな停止」か「CPUが燃える停止」かで、初動が変わります。前者は証拠採取の余地があり、後者はサービス影響が大きく、即時のストッパー(ブレーキ)が必要になりがちです。
ftrace:カーネル側の待ちを“線”として追う
ftraceは、カーネル内部のイベントを辿るための仕組みです。特に、I/O待ちやスケジューリング、ロック関連のイベントを追えると、「アプリのロック待ち」なのか「I/Oが詰まってロック保持が長引いている」のかが見えます。デッドロックはロック順序だけでなく、ロック保持時間の長さ(クリティカルセクション内でI/Oしている等)でも成立しやすくなるため、ここは重要です。
観測結果からの切り分け早見表
| 観測 | 示唆 | 次の一手 |
|---|---|---|
| straceでfutex待ちが継続 | ユーザー空間ロック待ちの可能性が高い | スレッドダンプで“誰が保持しているか”へ |
| perfでロック関数にサンプル集中 | スピン/リトライで進捗が止まっている | バックオフ・待機方式・競合点の縮小を検討 |
| ftraceでI/O待ちが長い | ロック保持中のI/Oが“詰まり”を増幅 | クリティカルセクション外へI/Oを逃がす設計 |
第5章で伝えたいのは、「万能ツール」ではなく「短時間で本質に近づく観測の組み合わせ」です。ここまで来ると、次章のカーネル側手掛かり(/proc/locks、lockdep、ログ)に進む準備が整います。カーネルが見ている待ち関係を使うと、循環の構造がより具体的になります。
カーネル側の手掛かり:/proc/locks・lockdep・ログから循環待ちを特定する
ユーザー空間のロック(pthread mutex など)は、アプリ側の情報がないと見えにくい一方で、ファイルロックやカーネル内部ロックは、OS側に“痕跡”が残ることがあります。ここでの狙いは、「止まっている」という現象を、カーネルが持つ観測点で裏取りして、議論のノイズカットをすることです。
心の会話で言うと、こんな切り替えになります。
「アプリだけ見ても分からないなら、OSが見ている待ち関係を借りよう」
「推測で議論が過熱する前に、“見えている事実”を増やして落ち着かせたい」
/proc/locks:ファイルロック(fcntl等)を扱うなら最優先の観測点
/proc/locks は、カーネルが管理しているファイル(レコード)ロックの情報を参照するための入口です。fcntl の F_SETLK / F_SETLKW などのアドバイザリロックを使う設計では、どのプロセスがどの範囲のロックを持っているかを追う手掛かりになります。
ただし、ここでの注意点は2つあります。
- “待っている側”の情報が常に十分に出るとは限らない:状況やロックの種類・カーネル実装によって、見え方が変わります。
- そもそもファイルロックを使っていないなら、/proc/locksは主役ではない:pthread mutex中心のデッドロックは、別の証拠(スレッドダンプ等)が重要です。
逆に言えば、EDEADLKが fcntl(F_SETLKW) 周辺で返っているなら、/proc/locks は「誰がどの資源を握っているか」を整理するための現実的な台帳になります。ここで資源(ファイル・範囲・順序)を言語化できるだけで、再発防止の設計議論が一気に前へ進みます。
lockdep:循環待ちの検出を“仕組みとして”持てるが、本番で常に使えるわけではない
lockdep は、カーネルのロック依存関係(どのロックをどの順序で取ったか)を追跡し、危険な順序や循環の可能性を検出するためのデバッグ機構です。これは非常に強力ですが、一般に カーネルの設定(CONFIG系)に依存し、常時有効化される前提ではありません。つまり「今すぐ本番でlockdepをONにして調べよう」と言えるケースは多くありません。
それでもlockdepの考え方は重要です。なぜなら、EDEADLKの世界観は「待って詰む」より前に、「危険な順序」を検出して止める、という発想に近いからです。現場にとっては、次のような“合意形成”の材料になります。
- 本番で常時は難しくても、検証環境で同等の負荷をかけて観測する価値がある
- 「ロック順序を設計に落とす」ことが、障害の鎮火(沈静化)につながる
ログ(dmesg / journal / アプリログ):EDEADLK周辺の“時間”を揃える
デッドロック調査で地味に効くのが、時間軸を揃えることです。EDEADLKが返った時刻の前後で、OS側でI/O遅延や一時的なエラーが起きていないか、OOMやスケジューラの異常がないか、といった「ロック保持時間を伸ばす要因」がないかを確認します。
ロックは、順序だけでなく保持時間でも成立しやすくなります。クリティカルセクション中にI/Oが入っていたり、外部APIが遅延したり、ストレージが詰まったりすると、「普段は成立しない循環」が成立しやすくなります。だから、アプリログとOSログの時刻を揃えるだけで、原因の輪郭がはっきりすることがあります。
“カーネル観測”を使うときの現場向けチェックリスト
- 入口はどれか:pthread系か、fcntl系か(EDEADLKの発生箇所で当たりを付ける)
- “誰が握っているか”が見えるか:スレッドダンプ//proc情報/ログのいずれかで照合する
- ロック保持時間を伸ばす要因がないか:I/O遅延・外部依存・GC・OOMなどを時刻で合わせる
第6章の結論は、「OS側の観測点で、循環や保持の事実を積み上げる」ことです。ここまで揃うと、次の章で“設計で勝つ”——ロック順序と構造そのものを整えて、同種障害を鎮火(収束)させる議論ができます。
設計で勝つ:ロック順序・階層化・粒度調整で“循環”を作らない
デッドロックは、現場から見ると「運が悪い」と感じやすいのですが、設計の目線ではかなり再現性のある“構造問題”です。循環待ちは、ロックを複数持てる設計で、取得順序が揺れるときに成立します。だから、最も強い対策は「循環が成立しない構造」にすることです。
心の会話はこうなります。
「また夜に固まるの、もう嫌だ。運じゃなくて構造で潰したい」
「“誰がどの順でロックを取るか”を、コードに任せずルールにしたい」
基本原則:ロックに“全順序”を与える(ロック順序の固定)
複数ロックを扱うなら、取得順序を固定します。たとえば A と B を同時に取り得るなら、必ず A→B の順で取る、というルールです。これだけで「Aを持ってB待ち」と「Bを持ってA待ち」が同時に成立しなくなり、循環が断ち切れます。
ここで重要なのは、ルールを文書だけに置かないことです。実装に落とし込みます。
- ロック取得をラップして、順序違反を検出できるようにする(検証環境で特に有効)
- “この関数はこのロックを持って呼ばれる”という契約を明示する
- レビューで「順序が守られているか」を必ず見るチェック項目にする
これができると、EDEADLKを見たときに「ルール違反がどこかにある」と追えるようになり、調査が速くなります。
階層化:ロックを“レイヤー”で分けて越境を禁止する
ロック順序の固定は強力ですが、システムが大きくなるとロックの数も増え、全順序の維持が難しくなります。そこで、ロックを階層(レイヤー)に分け、越境を禁止します。
- 上位ロック(例:グローバル設定)→下位ロック(例:コネクション単位)
- 同一階層のロックは同時保持しない(必要なら粒度を変える)
「どこまでが上位で、どこからが下位か」を整理すると、循環が成立する経路が減り、設計が読みやすくなります。結果として、障害対応の“説明コスト”も下がります。
粒度調整:大きすぎるロックは“保持時間”で事故る
大きいロックは実装が簡単で、最初は安全に見えます。しかし、ロック保持中にI/Oや外部呼び出しが入りやすく、保持時間が伸びると、別のロック待ちと絡んでデッドロックが成立しやすくなります。ここでの対策は、単純に「細かくする」ではなく、境界を整理して、保持中にやってはいけないことを決めることです。
たとえば次のようなルールは、現場で効きます。
- ロック保持中に外部APIを呼ばない
- ロック保持中にファイルI/Oをしない(ログ出力も含む)
- DBトランザクションとアプリロックを重ねない(重ねるなら順序と境界を厳格に)
これらは「性能」ではなく「進捗保証」のルールです。ここを守ると、デッドロックだけでなく、長時間停止やタイムアウト連鎖も抑え込みやすくなります。
設計パターンの比較:どれを選ぶべきかの整理
| アプローチ | 狙い | 向く状況 |
|---|---|---|
| ロック順序の固定 | 循環待ちを構造的に不可能にする | 複数ロックを持つ箇所が明確、レビュー文化がある |
| 階層化(レイヤー) | 全順序が難しい規模で秩序を保つ | コンポーネントが多い、責務分離が進んでいる |
| 粒度調整+境界ルール | 保持時間を減らして成立確率を下げる | I/O遅延が絡む、外部依存が多い |
第7章の帰結は、「デッドロックは“運”ではなく“秩序”で抑え込める」ということです。ただし、設計だけで100%を保証するのは難しい現実もあります。だから次章では、実装側での逃がし方——trylock、timedlock、バックオフなどで“必ず前に進む”仕組みを入れる方法を扱います。
実装で逃がす:trylock/timedlock/バックオフで進捗保証を入れる
設計で循環を作らないのが理想でも、現実には「レガシーで直しきれない」「外部ライブラリの都合で順序が固定できない」「運用要件でロック範囲が広い」などの制約があります。そういう時に効くのが、実装で“逃げ道”を作り、進捗保証を入れるアプローチです。EDEADLKを「検出できた瞬間」に、固まり切る前に手を打てる状態へ持っていきます。
心の会話はこうです。
「完璧に直すのは時間がかかる。まずは“固まらない”方向に軟着陸させたい」
「多少遅くてもいい。止まらないことが正義の局面ってある」
trylock:待たないことで循環を成立させない
trylock は「取れなければ待たずに戻る」ため、循環待ちの輪の中に入る前に抜けられます。重要なのは、取れなかったときの振る舞いを“仕様”として設計することです。
- すぐに失敗として返す(上位で再試行する/別経路へフォールバックする)
- 優先度の低い処理をスキップする(後で再実行できるなら有効)
- ロックが不要な読み取りに切り替える(キャッシュやスナップショットがある場合)
「取れなかったらリトライすればいい」と雑にすると、リトライ嵐で別の詰まりを起こすことがあります。だから次の timedlock / バックオフとセットで考えます。
timedlock:無限待ちを禁止し、観測と復旧の余地を作る
timedlock(一定時間待って取れなければ諦める)は、「永久待ち」を防ぎます。これにより、
- タイムアウト時に必ずログ(スレッドID、ロック識別子、処理ID)を残せる
- 上位で“安全なリセット”や“代替処理”に切り替えられる
- 監視・アラートに繋げて、現場が状況説明しやすくなる
という効果があります。これは単なるエラー処理ではなく、運用のダメージコントロールの仕組みです。EDEADLKが返る設計と同様に、「固まる前に戻る」ことが価値になります。
バックオフ:再試行は“間隔”が本体(同時多発を避ける)
trylockやtimedlockの後に再試行する場合、同時多発で再突入すると、競合が増えて逆効果になります。そこでバックオフ(待ち時間を設ける)を入れます。ここは“性能チューニング”というより、過熱した競合を沈静化させるための設計です。
実務では、次のような方針が現場で扱いやすいです。
- 短時間の再試行は少回数に制限する(例:数回)
- それでも取れないなら、処理をキューに戻す/遅延実行に回す
- 全体同時に再試行しないよう、ジッタ(揺らぎ)を入れる
ここまでやると、「ロックが取れない=致命的停止」ではなく、「取れないなら一旦戻って、別の形で進める」という実装になります。これが“進捗保証”です。
EDEADLKを受けたときの“現場で困らない”扱い方
EDEADLKを握りつぶすのではなく、運用で扱えるイベントに変換します。おすすめは次の3点セットです。
- 必ず記録:発生箇所、スレッドID、ロック識別子、処理ID、直前の主要イベント
- 必ず戻る:無限待ちに入らず、失敗として上位に返す(再試行は制御する)
- 必ず観測へ繋ぐ:同種が一定回数を超えたらアラート、または自動で状態ダンプ
これにより、障害は「突然固まって炎上」ではなく、「兆候を検知して温度を下げる(クールダウン)運用」に近づきます。
第8章の結論は、完全な修正が難しい状況でも“固まらない実装”に軟着陸させられる、ということです。次は第9章で、こうした対策が本当に効くかを確認するためのテスト(負荷・並行度・フォールト注入)を扱い、「再発しない」を証明する方向へ進めます。
テストで潰す:負荷・並行度・フォールト注入で「再発しない」を証明する
デッドロック対策の難しさは、「直したら起きなくなった気がする」が通りやすい点です。起きにくい不具合ほど、偶然の不発を“改善”と誤認しがちです。だからこそ第9章は、再発防止を“気分”ではなく“証拠”で語れる状態に持っていく話です。現場の疲弊を減らすためには、ここが最後の踏ん張りどころになります。
心の会話はこうです。
「またいつか起きる、を放置したくない。次は“証明できる安心”が欲しい」
「監視や運用でブレーキをかけるのは大事。でも、根っこはテストで抑え込みたい」
1) 並行度を上げる:スレッド数と競合点を意図的に過密にする
デッドロックは、並行実行のタイミングが噛み合ったときに成立します。したがって、テストでは「普段より並行度を高くする」「同じ資源を奪い合う状況を作る」ことが基本です。具体的には、
- ワーカースレッド数を増やし、同じロックに複数スレッドが集中する状況を作る
- ロック取得順序が分岐するコードパス(例外処理、早期return、リトライ)を通す割合を増やす
- 複数ロックの“同時保持”が起きる関数(A→B、B→Aになり得る箇所)を重点的に回す
重要なのは、性能ベンチマークのように「平均スループット」だけを見るのではなく、「一定時間内に進捗ゼロにならない」ことを合否に含めることです。デッドロックは性能ではなく進捗の問題なので、評価指標も合わせます。
2) “止まり”を検知する:ウォッチドッグ(進捗監視)をテストに組み込む
再発防止を語るうえで強いのは、テストが「止まった」ことを確実に検知し、証跡(スレッドダンプやログ)を残せることです。おすすめは、テストハーネス側にウォッチドッグを入れることです。
- 一定時間、成功応答や処理完了件数が増えない場合に異常と判定する
- 異常判定時に、スレッドダンプ/主要メトリクス/直近ログを自動取得する
- 取得後はテストを中断し、原因調査に必要な材料を固定する
これがあると、「止まったかもしれない」ではなく「この時点で進捗が止まり、スレッドはこの待ちにいた」と言えるようになります。現場の議論が過熱しにくくなり、ノイズカットにもなります。
3) フォールト注入:遅延・失敗・タイムアウトを“意図的に”混ぜる
本番のデッドロックは、外部要因がロック保持時間を伸ばして成立することがあります。そこでテストでは、外部I/Oや依存先に対して「遅延」「一時失敗」「タイムアウト」を意図的に混ぜます。目的は、
- ロック保持中にI/Oが入ったときに詰まらないか
- リトライが同時多発して競合が増幅しないか
- タイムアウト時にロック解放漏れが起きないか
を確かめることです。第8章で述べた timedlock やバックオフを入れている場合は、ここで「本当に“軟着陸”するか」を確認できます。再試行が嵐になるなら、バックオフの設計が不足している可能性があります。
4) 静的・動的検証:人間の目だけに依存しない
ロック順序の固定や階層化は、レビューで検出できることもありますが、規模が大きいほど漏れます。ここで効くのが、ツールによる検証です。環境や言語にもよりますが、一般論としては次が役立ちます。
- ロック取得をラップして、順序違反を検出する(検証環境のみに仕込む運用も現実的)
- スレッド解析系のツールやサニタイザ(言語・コンパイラ対応がある場合)でデータ競合やロック問題を炙り出す
- 統合テストで「進捗停止」を自動判定し、証跡を採る
ここでの姿勢は「万能な銀の弾丸を探す」ではなく、「人間が見落とす箇所を、仕組みで拾う」ことです。これにより、対策の“持続性”が上がります。
5) 合否基準を“運用の言葉”に落とす
最後に、合否基準を運用の言葉に翻訳します。たとえば、
- 一定時間内に進捗が止まらない(ジョブ完了が増え続ける)
- ロック待ちが閾値を超えたら必ずタイムアウトし、上位へ戻る
- タイムアウト時に必ずログと状態ダンプが残る
といった条件です。これがあると、現場は「次に同種が起きても、沈静化(被害最小化)させる手順がある」と感じられます。再発防止は“技術”だけでなく“安心”の設計でもあります。
次章では、ここまでの検出・観測・設計・実装・テストを一本に束ね、「EDEADLKを“仕様”にする」帰結へ進みます。
帰結:EDEADLKを“仕様”にする—「止まらない」より「必ず進む」設計へ
デッドロック対策で最終的に大事なのは、「絶対に起きない」と言い切ることではありません。現場システムはレガシーも混ざり、外部依存もあり、想定外入力も来ます。だから現実的な帰結は、「起き得る」を前提に、検出して、被害最小化して、必ず進捗を取り戻すことです。EDEADLKは、その世界観にフィットする“シグナル”です。
心の会話は、ここでこう変わります。
「ゼロにするより、起きた瞬間に抑え込み、進捗を守る方が現実的だ」
「“また固まるかも”の不安を、運用と設計で温度を下げていきたい」
1) EDEADLKを握りつぶさず、設計判断に使う
EDEADLKを単にログに出して終わりにすると、「たまに出る謎のエラー」になります。そうではなく、設計判断に使います。
- EDEADLKが返ったら、その処理は“失敗として切り上げる”のか
- 安全な再試行(バックオフ付き)へ切り替えるのか
- 代替経路(キャッシュ、遅延実行、キュー戻し)に逃がすのか
この判断を“仕様”にしておくと、現場は迷いません。迷いが減ると、障害対応は一気に沈静化しやすくなります。
2) 観測と運用を繋ぐ:兆候でアラートし、証跡を自動で残す
デッドロックは「起きた後に気づく」より、「起きる前の兆候(ロック待ち増加、タイムアウト増加、EDEADLK発生)」で気づける方が被害最小化に繋がります。そこで、
- ロック待ち時間やタイムアウト回数をメトリクス化する
- EDEADLKの発生率・発生箇所を集計する
- 閾値を超えたら、自動で状態ダンプや追加ログを採取する
という“観測→運用”の線を引きます。これにより、「固まってからの炎上」ではなく、「過熱する前に空気を落ち着かせる」運用に寄せられます。
3) 一般論の限界:最適解はシステム構成・負荷・運用制約で変わる
ここまで述べた内容は、デッドロックに対する有効な一般論ですが、個別案件では難所が出ます。たとえば、
- ロック順序を固定したくても、複数チーム・複数サービスの境界を跨いでいる
- 外部ライブラリが内部でロックを取り、順序を制御できない
- 性能要件が厳しく、粒度調整が副作用(スループット低下)を生む
- 本番での証跡採取(gdb attach 等)が運用リスクになり得る
などです。つまり、同じ「EDEADLK」「デッドロック」という言葉でも、最適な対策の組み合わせは変わります。だから最後は、一般論から一歩進めて「あなたのシステムのロック設計・運用設計としてどう落とすか」を決める必要があります。
4) 次の一歩:迷ったら、現場目線で一緒に設計する
もしあなたが今、具体的な案件(APIのタイムアウト連鎖、ジョブキューの詰まり、ログにEDEADLKが出始めた、オンコールが増えた、など)で悩んでいるなら、まずは“見える化と抑え込み”から始めるのが現実的です。そのうえで、設計(順序・階層・粒度)と実装(timedlock・バックオフ)を、運用要件に合わせて軟着陸させていきます。
こうした個別最適は、単発のテクニックより「システム全体の整合」を見て決める必要があります。重要データや基幹サービスが関わる場合は、判断ミスが長期停止や復旧難易度の上昇に繋がることもあります。株式会社情報工学研究所では、ログ・観測・構成・運用制約まで含めて、現場エンジニア目線で一緒に整理し、被害最小化(ダメージコントロール)と再発防止の両方を支援できます。一般論で迷いが出た時点で、株式会社情報工学研究所の様な専門事業者に相談することを検討してください。
付録:現在のプログラム言語各種における“デッドロック対策”の注意点
最後に、言語ごとに「起こりやすい落とし穴」と「現場で効く観点」を整理します。ここで扱うのは一般論であり、フレームワークや実装、実行環境(OS、ランタイム、ライブラリ)によって挙動は変わります。個別案件では、ログ・スレッドダンプ・構成を踏まえて判断する必要があります。
C / C++(pthread等)
- ロックの取得・解放が手動になりがちで、例外や早期returnで解放漏れが起きやすい(解放漏れは“永久待ち”に直結)
- ロック順序の固定・階層化を、設計ドキュメントと実装(ラッパ)で一致させることが重要
- 本番での調査はスレッドダンプ採取が効く一方、運用リスクもあるため手順を事前に決めておく
Rust(Mutex/Arc、async含む)
- 所有権によりメモリ安全性は上がるが、論理的なデッドロックは別問題として起こり得る
- Mutexガードの保持範囲が広いと、意図せず長時間保持になりやすい(特にI/Oや待機を跨がない設計が重要)
- async環境では「待機(await等)を跨いでロックを保持しない」など、保持境界のルールが重要になる
Go(goroutine、channel、sync.Mutex等)
- Mutexのデッドロックに加えて、channelの送受信待ちの循環(全goroutine待ち)が“停止”として現れることがある
- 「ロック+チャネル」を混在させると、順序が複雑化しやすい(ルール化と監視が重要)
- テストでは並行度を上げ、進捗監視(ウォッチドッグ)とスタックダンプ採取を組み合わせると原因が追いやすい
Java / Kotlin(synchronized、ReentrantLock等)
- モニタロックや複数ロックの組み合わせで循環待ちが起きやすい。取得順序の固定が基本
- 待機(wait/notify)や条件変数の誤用で“止まったように見える”状態が作られやすい
- スレッドダンプ(jstack等)で待ち関係が追いやすい反面、運用設計(採取タイミング・権限・影響)を決めておくと現場が楽になる
Python(threading、asyncio等)
- GILがあっても、I/Oや待機、複数ロックの循環は起こり得る(「Pythonだから安全」という誤解に注意)
- ロック保持中にI/Oを行うと保持時間が伸び、成立確率が上がる。保持境界を短くする
- asyncio等の非同期では、待機点を跨いだ資源保持が複雑化しやすいので、設計として“保持しない”を徹底する
JavaScript / Node.js(イベントループ、Worker等)
- 単一イベントループでも「待ち合わせの設計ミス」で進捗が止まる(Promiseの循環依存、完了条件の欠落など)
- Workerやネイティブ拡張を使う場合は、従来型のロック問題が入り込む(境界で設計を分ける)
- “止まる”がCPUバウンド(イベントループのブロック)なのか待機なのかを観測で切り分けることが重要
PHP(FPM/CLI、拡張、外部資源)
- 言語自体は一般にスレッド並行を前提にしないケースが多いが、外部資源(DBロック、ファイルロック、分散ロック)で待ちが発生し得る
- アプリ側の“ロック”より、DBやストレージ、分散KVSなどの待ちがボトルネックになりやすい(観測点を外に置く)
- 並列実行は「プロセス並列」で起きることが多いので、プロセス間の資源競合(ファイル、DB行、キュー)に注意する
C# / .NET(lock、async/await等)
- 同期ロックと非同期の混在で、待機と保持が複雑化しやすい(保持境界のルール化が重要)
- ロック内でのI/Oや待機が保持時間を伸ばし、競合を増やす。クリティカルセクションは短く
- スタックや待機状態を観測できる仕組み(ログ・ダンプ)を、運用手順として整えると再発時の収束が速い
付録のまとめとして、「言語が違っても、デッドロックは“順序”“保持時間”“待機の設計”で成立する」という点は共通です。一方で、観測の取り方(ダンプの採取、ログの残し方、運用リスク)は言語・ランタイムで変わります。一般論だけでは決めきれない場面が出たら、株式会社情報工学研究所の様な専門事業者に相談し、対象システムの構成・運用制約・復旧期限に合わせた“現実的な軟着陸”を一緒に設計することをおすすめします。
はじめに
Linuxシステムを運用していると、時折「EDEADLK (35)」というエラーに遭遇することがあります。このエラーは、リソースの競合やデッドロック状態によって発生し、システムの正常な動作を妨げる可能性があります。特に、複数のプロセスやスレッドが同じリソースにアクセスしようとした際に起こることが多く、適切な対策を講じていないとシステムの安定性に影響を及ぼす恐れがあります。この記事では、EDEADLKの基本的な定義と原因をわかりやすく解説するとともに、具体的な事例や対処法についても詳しく紹介します。システム管理者やIT担当者が安心してシステムを運用できるよう、現状の理解と適切な対応策を身につけることが重要です。データの安全性とシステムの安定性を確保するために、正しい知識と対策を備えることが求められます。
EDEADLK(エラーコード35)は、Linuxシステムにおいてリソースの競合やデッドロック状態が原因で発生するエラーです。デッドロックとは、複数のプロセスやスレッドが互いに相手の保持しているリソースを待ち続ける状態を指します。この状態になると、システムは待機状態から抜け出せず、最悪の場合システム全体の動作に支障をきたすことがあります。EDEADLKは、POSIX標準のエラーコードの一つで、「リソースの競合によるデッドロックが検出された」ことを示しています。 このエラーの背景には、複数のプロセスやスレッドが同じファイルやデバイス、メモリ領域などのリソースに同時アクセスしようとする際の管理不足や設計の問題があります。例えば、一つのプロセスがファイルAをロックし、その状態で別のプロセスが同じファイルをロックしようとした場合、互いに待ち状態に入り、最終的にシステムがエラーを返すことがあります。 また、EDEADLKは、システムの安定性を保つために、リソース管理において適切なロック制御やタイムアウト設定を行うことが重要であることを示しています。これにより、デッドロックの発生を未然に防ぎ、システムの正常な動作を維持することが可能となります。システム管理者やIT担当者は、これらの基本的な原因と仕組みを理解し、適切な対策を講じることが、システムの信頼性向上につながります。 ※当社は、細心の注意を払って当社ウェブサイトに情報を掲載しておりますが、この情報の正確性および完全性を保証するものではありません。当社は予告なしに、当社ウェブサイトに掲載されている情報を変更することがあります。当社およびその関連会社は、お客さまが当社ウェブサイトに含まれる情報もしくは内容をご利用されたことで直接・間接的に生じた損失に関し一切責任を負うものではありません。
EDEADLKの発生原因を理解することは、効果的な対策を講じるための第一歩です。具体的な事例として、複数のプロセスが同じファイルやリソースに対してロックを取得しようとする状況が挙げられます。たとえば、あるシステムでは、バックアップ処理とデータ更新処理が同時に同じデータベースにアクセスし、互いにロックを待つ状態に陥ることがあります。このような状況が繰り返されると、システムはデッドロック状態に入り、エラーコード35が返されるのです。 この問題を防ぐためには、リソースの管理方法を見直す必要があります。まず、ロックの取得順序を統一することが重要です。複数のプロセスが同じリソースを扱う場合、必ず同じ順序でロックを取得することで、循環待ちの状態を避けることができます。また、タイムアウト設定を導入し、一定時間内にロックが取得できない場合は処理を中断し、リトライやエラー通知を行う仕組みも有効です。 さらに、システムの設計段階でリソースの競合を最小化する工夫も必要です。例えば、リソースの分散化や、必要最小限のロック範囲に限定することで、デッドロックのリスクを低減できます。これらの対策は、システムの負荷状況や運用形態に合わせて適切に調整されるべきです。 最後に、定期的なモニタリングとログ分析も重要です。デッドロックの兆候を早期に検出し、原因を特定することで、未然にエラーを防ぐことが可能です。システム管理者やIT担当者は、これらの知識と技術を組み合わせて、安定した運用を維持していくことが求められます。 ※当社は、細心の注意を払って当社ウェブサイトに情報を掲載しておりますが、この情報の正確性および完全性を保証するものではありません。当社は予告なしに、当社ウェブサイトに掲載されている情報を変更することがあります。当社およびその関連会社は、お客さまが当社ウェブサイトに含まれる情報もしくは内容をご利用されたことで直接・間接的に生じた損失に関し一切責任を負うものではありません。
EDEADLK(エラーコード35)の具体的な対処法には、システム設計の見直しと運用上の工夫が不可欠です。まず、最も効果的な方法は、リソースのロック取得順序を統一することです。複数のプロセスが同じリソースを扱う場合、常に同じ順序でロックを取得することにより、循環待ちの状態を防止できます。例えば、データベースのテーブルやファイルのアクセス順序を決め、それに従って処理を行うルールを設けるのです。 次に、タイムアウト設定の導入も重要です。ロック取得に一定時間を超えた場合、自動的に処理を中断し、リトライやエラー通知を行う仕組みを整備することで、デッドロックの拡大を防止します。これにより、システム全体の応答性や安定性を維持できます。 また、リソースの分散化や、ロック範囲の限定も有効です。必要最小限の範囲だけにロックをかけることで、競合の可能性を減らし、デッドロックのリスクを低減します。例えば、データベースのトランザクション設計において、必要なデータだけをロックし、長時間のロック保持を避けることが推奨されます。 さらに、定期的な監視とログ分析も不可欠です。システムの動作ログを収集し、デッドロックの兆候や頻度を把握することで、根本原因を特定しやすくなります。これにより、事前に問題を察知し、適切な対応策を講じることが可能です。 最後に、システムの設計段階からこれらの対策を組み込むことが、長期的な安定運用を支える基本です。リソース管理の最適化と運用の徹底により、EDEADLKの発生を未然に防ぎ、システムの信頼性と効率性を高めることができます。
EDEADLKの問題を根本的に解決するためには、システムの設計段階から適切な対策を組み込むことが不可欠です。まず、リソースのロック取得順序を統一することが最も効果的な方法の一つです。これにより、循環待ちの状態を防ぎ、デッドロックの発生を未然に抑えることができます。例えば、複数のリソースにアクセスする処理では、常に同じ順序でロックを取得する規則を設けることが重要です。 次に、タイムアウトを設定することで、ロックの取得に長時間かかる場合のリスクを軽減できます。一定時間内にロックが取得できなかった場合は、処理を中断し、リトライやエラー通知を行う仕組みを導入することが推奨されます。これにより、システム全体の応答性や安定性を維持しやすくなります。 さらに、リソースの分散化やロック範囲の限定も有効です。必要最小限の範囲にロックをかけることで、競合の可能性を減らし、デッドロックのリスクを低減します。例えば、データベースのトランザクション設計においては、長時間ロックを避けるために、対象のデータだけを細かくロックする工夫が求められます。 また、定期的な監視とログ分析も重要です。システムの動作ログを継続的に収集し、デッドロックの兆候や頻度を把握することで、問題の根源を特定しやすくなります。これにより、早期に対策を講じ、システムの安定運用を確保できます。 最後に、これらの対策をシステム設計の段階から盛り込むことが、長期的な信頼性向上に直結します。適切なリソース管理と運用の徹底により、EDEADLKの発生を抑え、システムの効率と安定性を高めることが可能です。これらの取り組みは、システムの規模や運用状況に応じて柔軟に適用し続けることが重要です。
システムの安定性と信頼性を維持するためには、日常的な運用の中で継続的なモニタリングと改善が不可欠です。特に、デッドロックやリソース競合の兆候を早期に検知できる仕組みを整備しておくことが重要です。定期的なログの収集と分析により、どのリソースが頻繁に競合しているか、どの処理が長時間ロックを保持しているかを把握し、根本的な原因を特定します。こうした情報は、システムの設計や運用ルールの見直しに役立ち、未然に問題を防ぐための貴重な資料となります。 また、システムの運用においては、ロックの取得と解放のタイミングを適切に管理し、必要な範囲だけにロックを限定することも効果的です。これにより、リソースの競合が減少し、デッドロックの発生確率を低減させることができます。さらに、複雑なシステムでは、リソースの分散化や処理の並列化を検討し、リソースへのアクセスを効率化することも重要です。 こうした取り組みを継続的に行うことで、システムの運用効率は向上し、予期せぬエラーやダウンタイムを最小限に抑えることが可能です。最終的に、システムの安定性は、設計段階の工夫と日々の運用管理の両面からのアプローチによって支えられます。システム管理者やIT担当者は、これらのベストプラクティスを実践し、継続的な改善を心掛けることが、長期的な信頼性向上につながります。
本記事では、Linuxシステムにおいて頻繁に遭遇するEDEADLK(エラーコード35)の概要と原因、そして具体的な対策について詳しく解説しました。EDEADLKは、複数のプロセスやスレッドがリソースの競合やデッドロック状態に陥ることで発生し、システムの正常な動作を妨げる可能性があります。原因を理解し、ロックの取得順序の統一やタイムアウト設定、リソースの分散化といった対策を実施することが、システムの安定性を保つためには不可欠です。特にシステム設計の段階からこれらの対策を取り入れ、日常の運用においても継続的なモニタリングと改善を行うことが、長期的な信頼性向上につながります。システム管理者やIT担当者は、これらの基本的な知識と実践を通じて、システムの安定運用を支え、リスクを最小限に抑える努力を続けることが求められます。適切な対策と継続的な管理により、システムのパフォーマンスと信頼性を維持し、安心して運用できる環境を築いていくことが可能です。
システムの安定運用には、日々の適切な管理と継続的な改善が欠かせません。今回ご紹介したEDEADLKの対策や予防策は、システム管理の基本として重要なポイントです。もし、ご自身のシステムにおいて不安や課題を感じている場合は、専門的なサポートを受けることも検討されてはいかがでしょうか。信頼できるパートナーと連携し、定期的な監査や最適化を行うことで、システムのリスクを最小限に抑え、安定した運用を実現できます。私たちは、データ復旧やシステム保全の分野で多くの実績を持ち、安心してお任せいただける体制を整えています。まずはお気軽にご相談いただき、現状の状況やご要望をお聞かせください。適切なアドバイスとサポートを通じて、貴社のIT環境をより堅牢にしていきましょう。
EDEADLKに関する対策や予防策を実施する際には、いくつかの重要なポイントを押さえる必要があります。まず、システム設計段階でのリソース管理の最適化は非常に重要ですが、これには高度な技術的知識や経験が求められることがあります。誤った設計や設定は、逆にシステムのパフォーマンス低下や新たな問題の原因となる可能性もあるため、専門的な支援や十分な検証を行うことが望ましいです。 次に、タイムアウト設定やロックの範囲制限は有効な対策ですが、これらを適切に設定しないと、処理の遅延やデータの整合性に影響を及ぼすことがあります。特に、タイムアウト時間の設定はシステムの運用状況に応じて調整が必要であり、長すぎるとデッドロックのリスクが高まる一方、短すぎると正常な処理も中断されやすくなります。 また、システムの継続的な監視やログ分析についても、専門的なツールや知識が必要です。これらを怠ると、問題の早期発見や根本原因の特定が遅れ、結果的にシステムの信頼性に影響を及ぼす恐れがあります。さらに、運用ルールや管理体制の徹底も重要であり、担当者の経験や意識に依存しすぎると、見落としや不適切な対応につながることもあります。 最後に、システムの設計や運用においては、常に最新の情報やベストプラクティスを取り入れることが求められます。技術の進歩や業界の動向に伴い、最適な対策も変化します。したがって、一度の対策だけでなく、継続的な見直しと改善を行うことが、長期的なシステムの安定運用には欠かせません。これらの点を意識しながら、慎重かつ計画的に対策を進めることが重要です。 ※当社は、細心の注意を払って当社ウェブサイトに情報を掲載しておりますが、この情報の正確性および完全性を保証するものではありません。当社は予告なしに、当社ウェブサイトに掲載されている情報を変更することがあります。当社およびその関連会社は、お客さまが当社ウェブサイトに含まれる情報もしくは内容をご利用されたことで直接・間接的に生じた損失に関し一切責任を負うものではありません。
補足情報
※株式会社情報工学研究所は(以下、当社)は、細心の注意を払って当社ウェブサイトに情報を掲載しておりますが、この情報の正確性および完全性を保証するものではありません。当社は予告なしに、当社ウェブサイトに掲載されている情報を変更することがあります。当社およびその関連会社は、お客さまが当社ウェブサイトに含まれる情報もしくは内容をご利用されたことで直接・間接的に生じた損失に関し一切責任を負うものではありません。
