この記事は、前の記録の「回答編」です

前の記録(毎朝動いているはずのAI自動化41本を5.3日ぶん数えたら、最大91%が発火すらしていなかった)で、当社は一つ宿題を残しました。「メディア系ジョブは15分(900秒)で強制終了する設定のはずなのに、51分完走した記録がある。機序は未解明」——分かった気になって書くより、分からないと書いたところです。

その謎の一つの答えが、履歴の中から出てきました。結論から言います。タイムアウトは、記録の上では効いていました。しかし、実際のプロセスは死んでいませんでした。 エラー名が「timeout 900s」なのに、実測では139分間ぶら下がっていた記録が残っています。15分で止めたことになっているだけで、止まっていなかったのです。


用語を3つだけ

  • タイムアウト … 処理に制限時間を掛け、超えたら強制終了させる仕組みです。当社の自動化基盤は、各ジョブを子プロセスとして起動し、Pythonのsubprocessにtimeout=900秒(15分)を掛けています。超えたら記録上は status=error・error="timeout 900s" と残る設計です。
  • 子プロセスと孫プロセス … ジョブとして起動したプログラムが「子」。その子がさらに起動する重い処理(AIのCLIやブラウザ等)が「孫」です。今回の主役です。
  • ゾンビ的プロセス … 親を止めたのに死なずに居座り続けるプロセスを、この記事ではこう呼びます。次の起動をブロックしたり、同じ処理を重複させたりします。

見つかった矛盾の実データ

対象は、5分ごとに動くメール意思決定ジョブ(decide_mail)です。実測5.3日で468回起動し、うち27回がタイムアウトのerrorで終わっていました。その27回の中に、おかしな記録が2件ありました。2026年7月21日の実データです。

run_id 開始 終了 実時間 記録上のエラー
decide_mail-33beef9c39 01:20:03 03:24:02 124.0分 timeout 900s
decide_mail-9412483576 04:21:33 06:41:02 139.5分 timeout 900s

「900秒=15分で切った」という名前のエラーなのに、記録上の開始から終了まで2時間以上空いています。15分でタイムアウトを検知したはずの処理が、終了の記録を書くまでに、さらに2時間近くかかっていたことになります。


なぜこうなるのか

Pythonのsubprocessにtimeoutを掛けたとき、それが直接殺すのは起動した子プロセスだけです。その子がさらに孫プロセス——AIのCLIやブラウザといった重い処理——を産んでいると、親を止めても孫が生き残ることがあります。そして親側は、終了処理(後始末やハンドルのクローズ等)でブロックし、記録上の「終了」がずっと後ろにずれこみます。

つまり今回の記録は、こう読めます。15分の時点でタイムアウトの判定自体は下りた。しかし、産まれた孫までは殺しきれず、親の終了処理が2時間以上ブロックされ、終了の記録が139分後になった。 その間、実際のプロセスは生きていました。

同種の障害は、当社の別のアプリでも実際に起きています。「終了処理でハングし、exitイベントが来ず、親が永久にawaitし続ける」「(Node.jsは)オープンハンドルが残っていると、main関数がreturnした後もプロセスが終了しない」——言語もアプリも違いますが、構造は同じです。上位に掛けたタイムアウトは、子孫のプロセスまで確実に殺せるとは限らない。


何が困るのか

居座ったゾンビ的プロセスは、放っておくと2つの害を出します。次の起動をブロックすること、そして同じ処理を重複して走らせることです。前の記事で見た「発火したはずのジョブが抜ける」「実行が異常に長時間化する」という現象の、一因がこれです。5分ごとに動くはずのジョブの前に、2時間居座る先客がいたわけです。


正直に書く点

  • これは「タイムアウトが効いていない」バグです。そして、まだ完全には直せていません。 プロセスグループごと確実に殺す等の対策を検討している段階です。
  • 原因の全部を解明したとも言いません。 decide_mailがそもそも何に時間を食っていたのか——AIの応答待ちなのか、メール取得待ちなのか——の切り分けは、まだ途中です。分かったのは「記録上のタイムアウトと、実際のプロセスの生死は別物だった」というところまでです。

関連する記録


まとめ

  • 自動化基盤には15分(900秒)で強制終了するタイムアウトを掛けている。記録上も「timeout 900s」のエラーが残る。
  • しかし実データ2件で、開始から終了まで124分・139.5分という記録が見つかった。タイムアウトは"効いたことになっていた"だけだった。
  • 機序は、subprocessのtimeoutが殺すのは子プロセスだけで、孫プロセスが生き残り、親の終了処理がブロックされるため。当社の別アプリでも同じ構造の障害が起きている。
  • 居座ったプロセスは次の起動をブロックし、処理を重複させうる。前記事の「抜け・長時間化」の一因。
  • まだ直っていない。 プロセスグループごと殺す対策は検討中。何に時間を食っていたかの切り分けも途中。分かったところまでを、そのまま書いた。