収集元が止まったときだけ予備機へ切り替えるフェイルオーバーを組んだ

  • #自動化
  • #フェイルオーバー
  • #監視
  • #運用設計
収集元が止まったときだけ予備機へ切り替えるフェイルオーバーを組んだ

今回やったこと / この記事について

定時収集を1台だけに任せると、その機体が停止した時間だけデータが欠ける。そこで、常時二重実行するのではなく、主担当が収集していない時だけ予備担当が引き継ぐフェイルオーバーを実装した。対象は定時に実行する収集パイプラインで、主担当のheartbeat、収集時刻、実行中かどうかを判定材料にする。予備担当を置くこと自体ではなく、重複収集と状態の上書きを同時に防ぐことが中心である。

二重実行を避ける判定

今回の構成では、2台をactive-activeにはしない。収集した機体が共有ストレージの索引を作り直すため、同時に動くと互いの索引を上書きし、ローカル状態も分裂するからだ。予備担当は毎回、主担当のheartbeatを読み、should-runで次のどれかに分類する。

主担当の状態予備担当の動作理由
collectingまたはclaimでheartbeatが新しい待機主担当が実行中
最終収集が既定の鮮度内待機収集は止まっていない
heartbeatはあるが、待機中で収集時刻が古い収集動作中ではなく、収集だけ失敗した可能性がある
heartbeatが一度もない待機新しい版がまだ導入されていない可能性を排除できない

heartbeatが無い時に即座に引き継がないのは、障害検知の積極性より同時実行の回避を優先したためである。導入時は先に主担当を一度動かし、判定に使えるheartbeatを作る。これで「死んでいる」と「まだ仕組みが届いていない」を混同しない。

引き継ぎ直後の競合を止める

判定を通過しても、主担当が同じ時刻に起動する可能性は残る。そのため予備担当はclaimをheartbeatへ書いてから、もう一度yield-checkを行う。予備担当が主担当のclaimまたはcollectingを見たら譲る。逆に主担当は、予備担当が実際にcollectingへ進んだ場合だけ譲る。この非対称なルールにより、同時起動時の競合を抑え、主担当を優先しやすくする。

収集の後には、ローカルの.collect_stamp.jsonへ収集時刻と担当を記録する。共有状態を同期する前にこの印を更新し、共有側により新しい担当の状態がある時は古い状態で上書きしない。予備担当の成果を主担当の後処理が潰さないための、もう一つの安全弁である。

監視対象は「予備担当が動いたか」

予備担当は共有索引の更新時刻だけで主担当の稼働を判定してはいけない。予備担当が引き継いだだけでも索引は新しくなるため、次の回に予備担当自身が手を引く誤判定が起こる。判定対象は「主担当が収集中か」であり、両方が止まった状態は別の監視で扱う。予備担当のheartbeatが一度書かれた後、既定時間を超えて途切れた時だけ異常として扱う。未導入の段階から警告を連投しない設計も含めて、運用の状態を分けている。

試した環境・参照

  • Pythonの純関数判定と定時シェルパイプライン
  • フェイルオーバー判定用スクリプト
  • 定時実行パイプライン
  • launchd(またはsystemd)のサービス設定

やってみてわかったこと

フェイルオーバーは「別の機体でも同じ処理を実行する」だけでは不十分だった。主担当の生存、収集の成否、同時実行、状態の新しさを別々に記録し、引き継ぐ条件と譲る条件をコードに固定する必要がある。特にheartbeatが無い場合を停止とみなさない判断は、導入途中の誤作動を防ぐうえで重要だった。

更新履歴

  • 2026-09-18: 初稿。予備収集レーンの判定と状態引き継ぎを整理。

訂正履歴

なし。