自動化は「壊れる」より「静かに枯れる」で止まる — 在庫切れを鳴らす側の設計
この記事について
実装ステータス: 実装済み(複数の公開パイプラインで稼働中)
記事を時間差スロットで公開する仕組みを無人で回していると、典型的な障害とは違う止まり方をする。エラーは出ない。ヘルスチェックは200を返す。ただ出す弾(公開待ちの下書き)が無くなり、ある時刻から何も公開されなくなる。この記事は、その止まり方に気づいてから何を足したかの記録。
試した環境
- macOS 常駐機(launchd による毎時起動)
- bash + Python3(LLM は不使用。判定はすべて決定的なロジック)
- GitHub CLI(PR作成・自動マージの実行)
- 通知は Discord Webhook
なぜ「壊れる」ではなく「枯れる」なのか
時間差公開の仕組みは、装填(下書きを在庫として積む)と公開(在庫から1本を選んで公開する)を別の工程に分けて作ることが多い。この構成だと、公開側の処理自体は毎回正常に完走する。落ちるのは「その日に選べる在庫があるかどうか」という、処理の外側にある前提のほうだ。
一般的な死活監視は「処理が失敗したか」「HTTPが200を返すか」を見る。しかしこの種の障害は失敗として現れない。成功したのに、選ぶものが無かったという状態になる。ここを見ていないと、上流の装填が止まっていることに誰も気づけないまま日数だけが経過する。
実装:スロットごとに在庫を数え、0件を検知したら鳴らす
公開処理の先頭で、その時刻のスロットに対して在庫があるかを必ず確認するようにした。ゼロだった場合だけ通知を出す。
RESULT="$(python3 scripts/release_next.py --articles-dir "$DIR" || true)"
RELEASED="$(python3 -c 'import sys,json;print(json.loads(sys.argv[1]).get("released") or "")' "$RESULT")"
if [ -z "$RELEASED" ]; then
# 在庫0=弾切れ。1日1回だけ鳴らす(多重通知を防ぐため直近の通知日を記録)
if [ "$(cat "$AMMO_STATE" 2>/dev/null)" != "$JDATE" ]; then
notify "在庫が0件です。上流の装填処理が止まっていないか確認してください。"
echo "$JDATE" > "$AMMO_STATE"
fi
fi
ポイントは通知の読み方まで設計に含めたことだ。この通知が飛んだとき、公開処理そのものは正常に動いている。異常があるのは公開処理ではなく、その手前で下書きを積んでいるはずの工程のほうだと決めておく。つまりこの警告は、直接は監視していない別の工程を間接的に監視していることになる。
応用:スロットにも優先順位を持たせる
在庫が少ない期間ほど、「どのタイミングで出すか」の判断も重くなる。公開スロットを単純な時刻順ではなく、実測に基づく価値の高い順に並べ替え、在庫が少ない日は優先順位の高い枠だけを使うようにした。
| 状況 | 挙動 |
|---|---|
| 在庫が1本しかない日 | 最優先の1枠にだけ置く。下位の枠には回さない |
| 在庫がその日の残り枠の数に届かない | 今の枠は温存し、より良い枠が来るまで待つ |
| 最優先枠なのに在庫が0 | ここでだけ「本当の弾切れ」として通知する |
下位の枠で温存判断が起きても通知は鳴らさない。最優先枠でなお在庫が0のときだけが、本当に上流を疑うべきタイミングだからだ。ここを分けずに全部通知すると、日常的な温存判断まで警報として鳴り続け、本当に上流が止まったときの通知が埋もれる。
コストと制約
- 通知の重複を防ぐために「直近で通知した日付」を状態ファイルとして永続化する必要がある。これを忘れると、スロットの数だけ同じ警告が繰り返し飛ぶ
- この設計は「在庫が尽きたこと」しか検知しない。装填処理そのものが失敗している場合、その原因(PRが検証に落ちている、素材が足りない等)は別途、装填側のログを見に行く必要がある。枯渇通知は「どこかで止まった」という一次情報でしかない
- スロットの優先順位は実測データに基づいて決めているため、参照するデータが古くなると判断がずれる。定期的に測り直して並びを更新する前提の設計であり、一度決めて終わりにはできない
やってみてわかったこと
無人で回す系では、「異常を鳴らす」より「供給が細ったことを鳴らす」ほうが実際に効いた。処理そのものの正常性と、処理に渡される材料の充足度は別のレイヤーで、後者は前者の監視だけでは絶対に見えてこない。エラーが出ないまま止まる仕組みを運用するなら、供給側を専用に見張る仕組みを別に持つ必要がある。
更新履歴
- 2026-07-31: 初出。