対象は CentOS 6 に付属の cronie-anacron-1.4.4。
開始時間についての問題
/etc/anacrontab
がRHEL 6/CentOS 6系のデフォルトのまま以下のようになっていて、
# the maximal random delay added to the base delay of the jobs
RANDOM_DELAY=45
# the jobs will be started during the following hours only
START_HOURS_RANGE=3-22
#period in days delay in minutes job-identifier command
1 5 cron.daily nice run-parts /etc/cron.daily
7 25 cron.weekly nice run-parts /etc/cron.weekly
@monthly 45 cron.monthly nice run-parts /etc/cron.monthly
anacron 自体の起動も、デフォルトのまま /etc/cron.d/0hourly
と /etc/cron.hourly/0anacron
によって毎時1分に起動されているとする。
このとき、上記の cron.weekly
ジョブの開始時間の範囲は、何時何分から何時何分までになるか?
誤った回答
まず遅延時間を考えると、
- デフォルトの最短遅延が6分、最長遅延は上記の
RANDOM_DELAY
で45分 -
cron.weekly
の固定の遅延が25分 - あわせると、
cron.weekly
の取り得る遅延は31分〜70分
で、
- ジョブが実行される時間の範囲は、
START_HOURS_RANGE
により3時から22時の間 - anacron 自体は、毎時1分に起動する
ことから、ジョブが実行できる状況なら 3:32〜4:11 の間のどこかで起動する……
というのは誤りである。
正しい回答
anacron のソースで START_HOURS_RANGE
のチェックを行っている箇所を見てみると、以下のようになっている。
time_t jobtime = start_sec + job_array[j]->delay * 60;
t = localtime(&jobtime);
if (range_start != -1 && range_stop != -1 &&
(t->tm_hour < range_start || t->tm_hour >= range_stop))
{
/* ジョブをスキップする処理 */
この if
にマッチした場合 START_HOURS_RANGE
の範囲外としてジョブがスキップされるが、この基準となる時間は anacron の起動時間(ソース中の start_sec
)ではなく、それにジョブごとの遅延時間(ソース中の delay
)を加えた時間になっている。定義からすると当然のことで、遅延も考慮に入れた上で、ジョブが実際に動作する時間で判定しているわけだ。
なので、最初の問題の場合、2:01 の anacron の実行時に遅延が59分以上になったジョブは開始時間が 3:00 以降になるため、スキップはされず、そのまま 2:01 の回の anacron によって実行される。この場合は 3:00〜3:11 のどこかで起動することになる。
つまり、この設定での cron.weekly
のジョブが起動する時刻は、
- 3:00〜3:11 (2:01の回の anacron で起動された場合)、または
- 3:32〜4:11 (3:01の回の anacron で起動された場合)
のどこか、というのが正解になる。
派生する問題
デフォルトの設定の場合、cron.daily
ジョブの遅延時間は11分〜50分なので、2:01 の回の anacron で起動されることはなく、必ず 3:01 の回で起動する(3:12〜3:51 の間)。
しかし、anacron の動作として
-
/var/spool/anacron/
以下にあるジョブ名と同じ名前のファイルを読み、その中に書かれている前回実行日付から、ジョブを実行対象とするかどうかを判定する。たとえば週次ジョブなら、/var/spool/anacron/cron.weekly
ファイルに記載の前回実行日から7日経過しているかを判定する。 - 所定の日数が経過していて実行対象と判定されたジョブについて、この前回実行日ファイルをロックする。
- この後で、前述の
START_HOURS_RANGE
のチェックを行う。実行対象外と判定されたジョブについても、anacron 自体の完了まで前回実行日ファイルはアンロックされない。
という順序になっているため、前述のように 2:01 の回の anacron が 3:01 以降まで cron.weekly
の起動待ちになっている場合、実際には起動されない cron.daily
の前回実行日ファイルも一緒にロックされたままになっている。
この状態で 3:01 の回の anacron が cron.daily
を起動しようとすると、上の 2. のロックに失敗し、ログに
Job 'cron.daily' locked by another anacron - skipping
と出力して起動をスキップしてしまう。結果、cron.daily
ジョブは次回の起動タイミング 4:12〜4:51 まで起動が遅れることになる。
anacron で日次ジョブを実行しているが、なぜか特定の曜日だけたまに起動時間が1時間くらい遅くなる……のようなことがあったら、おそらくこういう挙動が原因と思われる。
その他
anacron の起動メカニズムや挙動については、以下がわかりやすい。
ただ、
「遅延時間」=共通設定にてランダム決定した遅延時間+
ジョブ個別設定にてランダム決定した遅延時間
とあるが、ジョブ個別設定の方の遅延時間は、ランダムではなくそのまま加算だと思われる。
あと、Red Hatの公式ドキュメント等には
RANDOM_DELAY — ジョブごとに指定されている delay in minutes 変数に追加される最大の時間分数。
最短の遅延時間は、デフォルトで 6 分に設定されています。
とあるが、6分に調整するロジックがソースのどこにも見つからない。ソース上は、
int i = random();
double x = 0;
x = (double) i / (double) RAND_MAX * (double) (atoi(value));
random_number = (int)x;
Debug(("Randomized delay set: %d", random_number));
のように 0 から RANDOM_DELAY
までの値をとるようになっていて(value
は RANDOM_DELAY
の設定値)、これ以降も何か調整している形跡がない。最低6分なら、デフォルト設定では cron.daily
の遅延は必ず11分以上になるはずだが、手元の cron ログでは
/var/log/cron:Feb 9 03:01:01 myhost anacron[25024]: Will run job `cron.daily' in 9 min.
というのがあるので、マニュアルの方が間違っているんじゃないかと思う。