結論から書きます
当社(株式会社TOE)は、問い合わせフォームへ自動送信するNode.js製の営業支援ツールを無人運用しています。平日、launchd(Macの定時起動の仕組み)で毎朝8時に自動起動し、夕方18時に停止する設計です。
ある月曜、このツールが「終わったのに終わらない」状態に陥りました。18時に処理は正しく止まったのに、プロセスがexit(終了)しないまま居座り、①その日の日次レポートが送られず、②翌朝8時の自動起動まで止まりました。 居座ったゾンビが、翌日の自分を締め出したのです。
打った手は、原因の完全解明ではありません。「原因が何であれ、時間が来たら必ず終わる」仕掛けを5重にかけることでした。その一次記録です。
用語を3つだけ
- launchd(ローンチディー) … Macで「毎朝8時に起動」のような定時実行を担う仕組みです。同じ名前(label)のプロセスがまだ生きていると、次の起動をかけません。 今回の障害の急所です。
- ゾンビ化 … 処理はすべて終わったのに、プロセスがexitせず居座り続ける状態を、この記事ではこう呼びます。
- オープンハンドル … 開いたままの接続やタイマーのことです。Node.jsは、これが残っているとmain関数がreturnしてもプロセスが終了しません。 「終わったのにexitしない」が起きる土台です。
何が起きたか
月曜18時、送信バッチは予定どおり処理を停止しました。ところが、ブラウザの後始末などの終了処理でハング。 終了イベントが親プロセスに届かず、親は永久に待ち続けました。その日の日次レポートは飛ばず、翌朝のlaunchdは同名プロセスが生きていたため再起動せず、ツールは丸一日止まりました。金曜も同じ障害が再発し、月曜に人が手動で再起動しました。
根因として判明しているのは「終了処理でのハング」までです。メール受信確認(IMAP)のハングも疑われましたが、どのハンドルが最後まで残っていたのかは特定できていません。 そこは正直に、わからないと書きます。
打った手:5重の「必ず終わる」
設計思想は一つ、「原因不問で、必ず終わる」です。
| # | 層 | 内容 |
|---|---|---|
| 1 | 送信バッチ全体 | 終了予定時刻+20分で強制終了(SIGKILL)するタイマーを付けた |
| 2 | メール受信確認(IMAP) | 接続15秒・全体45秒のタイムアウトを付けた |
| 3 | 日次レポート処理 | 全体を90秒で打ち切るようにした |
| 4 | main処理の最後 | 必ず process.exit(0)(明示的な終了)を呼ぶようにした |
| 5 | 最後の保険 | 終了予定+60分で問答無用にプロセスを終わらせる見張りタイマーを足した |
疑わしい処理(2・3)→正常系の出口(4)→バッチ全体(1)→最終防衛線(5)。どこか1枚が破られても、次の層が受け止める構えです。
もう一つ、日次レポートを独立したジョブに切り出しました。 launchdで平日18:05に単独起動させます。本体がハングして死んでも、その日の報告だけは別経路で必ず届きます。「本体の生死」と「報告の生死」を切り離しました。
なぜ「原因解明が先」にしなかったのか
「なぜ固まるのか」を完全に突き止めてから直そうとすると、突き止めるまでの間ずっと止まったままになるからです。実際に月曜と金曜の2回、丸一日ずつ止まりました。だから順番を逆にしました。先に「理由が何であれ、時間が来たら必ず終わる」を何重にも保証し、原因の追跡は動かしながら続ける。これは、前の記録(15分で止めるはずのAI処理が、記録の上だけ死んで、実際は139分生きていた)で観測した"プロセスが素直に死なない"問題に対する、実運用側の答えです。あちらが「観測」、この記事が「対策」にあたります。
正直に書く点
- これで「二度と止まらない」とは保証できません。 多層防御は"止まりにくくする"ものであって、完璧ではありません。
- この運用は他にも難しさを抱えています。例えば、再起動を繰り返すと処理がリストの先頭からやり直しになり、成果がゼロに見えてしまう問題です。止めない工夫と、止まっても進みを失わない工夫は別物で、後者はまだ課題です。
- 根因の全容——最後まで残っていたハンドルが何だったのか——は特定しきれていません。銀の弾丸ではなく、箍を何重にもかけた、というだけの記録です。
関連する記録
- 15分で止めるはずのAI処理が、記録の上だけ死んで、実際は139分生きていた — 同じ「死なないプロセス」問題の観測編。本記事はその対策編。
- 毎朝動いているはずのAI自動化41本を5.3日ぶん数えたら、最大91%が発火すらしていなかった — 居座るプロセスが「次の起動を止める」害を数字で見た全数調査。
まとめ
- 平日無人運用の営業支援ツールで、処理は終わったのにexitしない「ゾンビ化」障害が起きた。launchdは同名プロセスが生きていると再起動しないため、翌朝の自動起動まで止まった。
- 根因は終了処理でのハング。残ったハンドルの全容は未特定。
- 対策は「原因不問で必ず終わる」5重の多層防御(+20分SIGKILL/IMAP15秒・45秒/レポート90秒/process.exit(0)/+60分の最終見張り)。加えて日次レポートを平日18:05の独立ジョブに切り出した。
- 教訓:原因の完全解明を待つと、その間ずっと止まる。先に「時間が来たら必ず終わる」箍をかけ、追跡は動かしながらやる。 ただし銀の弾丸ではない。