0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

NTPが死んだら何が起きる? cronジョブが重複起動するシーン

Posted at

NTP serverが死んだら何が起こるんだろう?という疑問をしばらく考えていたのですが、一旦まとまったのでメモし
ます。

cronで定期実行しているシステムで、NTP(Network Time Protocol)などによるシステム時刻のジャンプが発生すると、ジョブが多重起動するリスクがあります。

この記事はこの素敵な記事を思いっきり参考にしています。詳細はこちらへ。


💡 この記事の前提:cronジョブ重複起動の根本原因は「次の実行時刻の即時到来」

cronの動作はシンプルで、現在時刻がcrontabに記述された時刻と一致するかをチェックし、一致すればジョブを起動する。

時刻が未来へジャンプしたとき、実行中のジョブが存在すると、以下のシンプルな条件で多重起動が起きる。

Job A が実行中 ∧ (現在時刻≥Job A の次の予定時刻)

簡単にいうと、Jobが実行中に、次にそのJobが起動される直前に時刻が飛ぶと、重複起動されます。

この原理は、ワイルドカードジョブ(シーン1)にも定刻ジョブ(シーン2)にも共通します。


😱 シーン1: ワイルドカードジョブにおける「次の実行時刻の即時到来」

ワイルドカード(*)を含むジョブは、時刻が飛んだ際、cronのリカバリ対象にはならない。そのため、時刻ジャンプがそのまま次の実行時刻の到来として機能し、重複起動を招く。

シナリオ: 毎時0分ジョブの実行中に時刻が飛ぶ

  • 設定: 毎時0分に実行されるJobを 0 * * * * で設定している。
  • 事象:
    1. 10:00:00Job A が起動(実行時間45分)。
    2. Job Aの実行中10:05:00)に、システム時刻が 10:59:00 へとジャンプする。

発生する挙動

  1. Job A は継続: 実行中の Job A は動作を継続する。
  2. 次の時刻が到来: 1分後11:00:00 が Job A の次の実行予定時刻となる。
  3. 同時実行: cronは 11:00:00 になると、Job B通常通り起動する。

Job Aが完了する前に Job Bが起動し、重複する。

図解:ワイルドカードジョブの重複

image.png


😱 シーン2: 定刻ジョブにおける「次の実行時刻の即時到来」(3時間以上のズレ)

特定の時刻に設定された定刻ジョブも、時刻のズレが3時間以上の場合は、cronはそれを時刻の修正と見なし、リカバリ(スキップされたジョブの補填)は一切行わない。しかし、次の実行予定時刻がすぐに訪れることで、やはり重複が発生する。

シナリオ: 実行中のJobがある状態で翌日へ大幅ジャンプ

  • 設定: 日次ジョブ(Job)を毎日 2:00 に実行するように設定している。
  • 事象:
    1. 1/1 2:00Job A が起動し、まだ実行中の状態にある(例:1/1 2:05)。
    2. システム時刻が 1/2 1:59 へとジャンプする。(時刻の進行が23時間54分であり、3時間以上のズレにあたる)

発生する挙動

  1. Job A は継続: 実行中の Job A は動作を継続する。
  2. リカバリはスキップ: 時刻のズレが3時間以上のため、間の期間にスキップされたはずのジョブは全て無視される。
  3. 次の時刻が到来: 時刻が 1/2 1:59 となり、1分後1/2 2:00 が Job A の次の実行予定時刻として到来する。
  4. 同時実行: cronは 1/2 2:00 になると、Job B1/2 2:00 実行分)を通常通り起動する。

実行中の Job A と、次の実行予定時刻で起動した Job B が重なり、リカバリの有無に関係なく重複が発生する。

図解:定刻ジョブの重複

内容は シーン1 と同じなので、こっちはちょっと違う見せ方で描いてみる
image.png


✅ 重複起動の防止策

時刻ジャンプの幅やジョブの種類に関わらず、最も確実な対策はジョブ自体に排他制御を実装すること。

1. ロックファイルによる排他制御(flock

flockコマンドを利用したロックファイルでの排他制御は、シンプルかつ最も堅牢。

# ロックファイルパスを指定 (ジョブごとに一意であること)
LOCK_FILE="/var/run/my_scheduled_job.lock"

# -n (non-blocking) でロックを試行。ロックできたら後続のコマンドを実行
if ! flock -n $LOCK_FILE -c "/path/to/your/actual_script.sh"; then
    echo "$(date): Job is already running. Exiting."
    exit 1
fi

flockはOSのファイルロック機能を利用するため、信頼性が高く、ワイルドカードジョブ、定刻ジョブのいずれの重複起動にも対応可能です。

2. ジョブの冪等性(Idempotency)の確保

「複数回実行されても、システムの最終的な状態が変わらない」という冪等性を持たせる設計も非常に重要。

  • データベース操作: INSERTではなく UPSERT(更新または挿入)を適用し、データの重複挿入を防ぐ。
  • 処理対象の限定: 処理対象データを、実行時刻のような曖昧な情報ではなく、確定的なキー(処理対象日付など)に基づいてフィルタリングし、重複実行時の影響を最小限に抑える。

結論

NTPが死んだ後、死んだままならこの問題は起きません。問題なのはNTPが死んだ後に 復活する時 です。復活させる前には、やばい cron がいないか確認してから復活させましょう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?