連続SKIPで処理が6日止まる:fail-closedの監視設計

消耗品を描いたアイキャッチ画像

cronは起動している。プロセスはexit 0で終わる。ログにはエラー行も残っている。それでも、当番ジョブは約6日間、一件も調査を完了していませんでした。

安全側に倒すfail-closedは、状態を信頼できないときに処理を止めます。この判断自体は正しいものです。ただし、止めた事実を誰も数えず、誰にも知らせなければ、設計は静かにfail-silentへ変わります。

今回の当番ジョブは、共有の状態ファイルを読んでから処理を始める構成でした。状態が読めなければSKIPし、何も変更せずexit 0で終えるため、破壊的な処理や不要な消費は起きませんでした。一方で、新規のwarn/criticalイベントは8件滞留しました。安全に、何もしなかったわけです。

目次

exit 0でも約6日間処理が止まった

監視していたのは、cronが起動したこと、プロセスが正常終了したこと、JSON parse errorのログ行でした。見ていなかったのは、実際に何件の仕事を完了したかです。

exit 0はプログラムが定義した正常な終了経路を通ったことを示します。処理対象を見つけたこと、状態が新鮮だったこと、通知まで届いたことは保証しません。fail-closedなジョブでは特に、SKIPしてexit 0という経路が正当であるため、プロセス死活だけでは構造的に盲目になります。

以前扱った自己申告を検証するための考え方と同じく、完了したという表示をそのまま信じるのではなく、外から確認できる完了の証跡を持つ必要があります。

共有ファイル競合と鮮度切れを分けて直す

最初の層は、共有ファイルへの直接リダイレクトでした。状態書き出しジョブは短い間隔で動き、当番ジョブは別の周期で動いていましたが、両者の起動位相は毎時0分に重なっていました。

shellで> shared.jsonのように直接書き出すと、書き込み前に対象ファイルは0バイトへtruncateされます。当番ジョブがこの窓で読めば、JSONとして解釈できずSKIPします。2026-07-04以降、毎時0分に起動する処理のほとんどがこの経路へ入り、2026-07-10に発見されました。

ここでアトミック書き込み、つまり一時ファイルへ書いてからrenameする方法を考えたくなります。これは競合窓を縮める有効な対策です。しかし今回、それだけでは復旧しませんでした。

第二の層はデータの鮮度です。復旧時には共有JSONのサイズは408バイトへ戻っていましたが、内容のageは74.9分で、consumer側の上限30分を超えていました。競合を外して読めても、古い状態を根拠に処理しないというfail-closedの判断は正しいため、SKIPは続きます。

恒久対策は、生の共有ファイルをconsumerが直接読む構成をやめることでした。状態を所有し、検証まで行うプログラムの出力へ読み取り先を付け替え、競合と鮮度の責務をデータ面で統合しました。cron時刻、しきい値、debounceは変更していません。同じ入力で通常実行は約96秒・exit 0となり、滞留していた8件を処理して完了しました。

消費0%を正常判定に使わない

図解: 消費0%を正常判定に使わない(起動フラグと実装のデプロイずれ / 引数解析の時点でexit 2 / 約13回連続で即死 / 消費0% / 表示対象のレーンが動いていない / 運用上の健全性を意味しません)
図解: 消費0%を正常判定に使わない(AI生成)

別の定期ジョブでも、起動フラグと実装のデプロイずれにより、引数解析の時点でexit 2となる状態が約2日間続きました。約13回連続で即死しており、ログにはエラーが整然と追記されていました。

内部ダッシュボードの「消費0%」という表示は正確でした。ただし、表示対象のレーンが動いていない以上、その正確さは運用上の健全性を意味しません。誰も読まないログへのappendは、実質的には無監視です。

正常終了・ログ・完了時刻を比較する

監視方法 適用条件 避ける条件 見落としやすい点
プロセス終了コードだけを監視する 単純な一回限りの処理 SKIPが正規経路にある定期ジョブ exit 0でも処理量がゼロになり得る
ログへエラーを残す 調査時に詳細が必要な運用 即時検知をログ閲覧へ依存する運用 ログは読まれなければ通知にならない
連続SKIP・最終完了・処理量を監視する fail-closedや非同期処理を安全に運用したい場合 指標を出せないまま監視だけ増やす場合 SKIPと完了を別イベントとして設計する必要がある

fail-closedを採用しているなら、三つ目が必要です。安全に止まることと、止まり続けていることを検知できることは別の要件です。

ストリーク警告を設ける場合、通知経路そのものが届くかも別途確認してください。通知の失敗まで監視の外に置くと、警告はまた静かになります。通知経路を先に検証する記録は、その切り分けに役立ちます。

連続SKIP回数と最終完了時刻を記録する

実装上の要点は、SKIPを単なるログ文ではなく、数えられるイベントとして扱うことです。少なくとも、SKIP回数、連続SKIP回数、最後に一件でも実処理を完了した時刻を持ちます。状態ファイルのfreshnessと、業務のcompletion markerは分けてください。状態が新しくても仕事が完了していないことはありますし、その逆もあり得ます。

  • fail-closedのSKIPを回数として記録しているか
  • 連続SKIP N回、または最終成功からN時間で警告できるか
  • 処理量ゼロを正常値として放置していないか
  • 共有ファイルへの書き込みはatomicか。そもそも生ファイルを直接読む必要があるか
  • producerとconsumerの起動位相が不用意に重なっていないか

結論は単純です。安全のためのSKIPは一回なら正しい振る舞いです。しかし連続SKIPは障害です。cronが生きているか、exit 0かだけで安心したいホームラボ運用者ほど、「最後に何件を完了したか」と「ゼロが続いていないか」を監視対象に加えるべきです。静かな成功終了ほど、業務の完了で裏取りしてください。

この検証を回している環境

この検証は、自宅の常設ラボ(使い捨てVM/LXCを回す母艦+GPU+VLAN分離ネットワーク)で動かしています。使っている機材と選定理由、全体構成は1本にまとめています。

ラボ構成のまとめを見る →
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

目次