Antigravity 2.0 のデスクトップが複数エージェントを並列に走らせ、背景でタスクを自動スケジュールするようになってから、私は繰り返しのジョブを次々と背景実行へ移しました。個人開発のアプリと Dolice のブログ群で、生成・検証・整合性チェックといった処理が毎日決まって動きます。手元で待たなくてよいのは、素直に楽です。
けれど、しばらく回して気づいたのは、深夜の同じ時刻にジョブがまとめてこける日がある、ということでした。原因は外ではなく自分にありました。新しいジョブを足すたびに、私は無意識に20時や19時半といった丸い時刻を選んでいました。その結果、同時刻に処理が集中し、限られた枠を食い合って、後半のジョブが起動直後に弾かれていたのです。
ここで扱うのは、背景スケジューリングで自分でピークを作らないための時刻分散の設計です。実際に自分のスケジュールを組み直した過程とあわせて整理します。
背景実行は「いつ動くか」の責任を渡してくる
手元で実行していたころは、自分が起動した瞬間が実行時刻でした。自然とばらけます。背景実行に移すと、その「いつ」を自分で決めて渡すことになります。ここで無頓着に丸い時刻を選ぶと、複数のジョブが同じ瞬間に立ち上がります。
便利さと引き換えに、実行時刻を設計する責任が手元に来ている、と捉えるのが正確でした。背景に任せたつもりでも、時刻の集中は自分が仕込んでいます。
自分で作るピークの正体
人は時刻を選ぶとき、丸い数字に寄ります。20時、21時半、正午。複数のジョブを別々のタイミングで足しても、選ばれる時刻が同じ丸い数字なので、結果として一点に集まります。
| 寄りやすい時刻 | 集まりやすい理由 | 起きること |
| 丸い時(20:00 等) | 覚えやすく指定しやすい | 複数ジョブが同時起動して枠を食い合う |
| 正午・深夜0時 | 区切りとして選ばれやすい | 他の定期処理とも重なりやすい |
| 就寝前の固定時刻 | 生活リズムに合わせがち | 夜のピークが厚くなる |
私の場合、夜の20時前後と19時半前後に処理が寄っていました。1本ずつ見れば軽いのに、同時に走ると枠を奪い合い、後半が弾かれます。ピークは外から来たのではなく、時刻選びの癖が積み上がって出来たものでした。
分散の設計 — 時刻をずらす3つの基準
散らすといっても、やみくもにずらすと今度は把握できなくなります。私は3つの基準で時刻を決めています。
- 丸い時刻を避け、分単位で意図的にずらす
- 重い処理どうしを同じ時間帯に置かない
- 既存の定期処理(キャッシュ処理や他の自動化)のピークを避ける
この3つを守るだけで、同時起動はかなり減りました。特に効いたのは、夜の同じ時刻に寄っていた重い処理を、いくつか別の時間へ動かしたことです。本数は変えず、時刻だけをずらしました。同時に走らなければ、限られた枠でも取り合いは起きません。
コードで散らす — ジョブ名から決定的にオフセット
手で1本ずつ時刻を決めると、また丸い数字へ寄ってしまいます。私はジョブ名から決定的にオフセットを算出し、機械的に散らしています。同じジョブ名なら毎回同じ時刻になるので、把握しやすさも保てます。
#!/usr/bin/env bash
# ジョブ名から決定的に分オフセットを算出し、丸い時刻への集中を避ける
set -euo pipefail
job_name="$1" # 例: "blog-verify"
base_hour="$2" # 希望する大まかな時(例: 20)
# ジョブ名のハッシュから 0-59 の分オフセットを決定的に作る
hash="$(printf '%s' "$job_name" | cksum | cut -d' ' -f1)"
minute=$(( hash % 60 ))
# さらに丸い分(0/15/30/45)を避けて必ずずらす
case "$minute" in
0|15|30|45) minute=$(( minute + 7 )) ;;
esac
minute=$(( minute % 60 ))
printf 'schedule %s at %02d:%02d\n' "$job_name" "$base_hour" "$minute"
# この時刻を背景スケジュールに登録する
同じ大まかな時を指定しても、ジョブ名が違えば分が散ります。丸い分に当たったときは必ず数分ずらすようにしてあるので、複数のジョブが偶然0分に揃うことも避けられます。手で選ぶ癖を仕組みで打ち消すのが狙いです。
重なりを禁じる — 前の枠が終わっていなければ見送る
時刻を散らしても、処理が長引けば前の実行と次の実行が重なります。重なった瞬間に枠の取り合いが復活するので、私は「前の枠がまだ動いているなら、次は見送る」というガードを入れています。
#!/usr/bin/env bash
# 同一ジョブの多重起動を防ぐ簡易ロック
set -euo pipefail
lock="/tmp/agent-${1:?job name required}.lock"
if [ -e "$lock" ] && kill -0 "$(cat "$lock")" 2>/dev/null; then
echo "previous run still active: skip this window" >&2
exit 0 # 失敗ではなく見送り
fi
echo $$ > "$lock"
trap 'rm -f "$lock"' EXIT
# ここで本体の処理を実行する
このガードで、深夜に処理が長引いた翌枠での二重起動が消えました。見送りをエラーにせず exit 0 で正常終了にしておくのが要点です。重なりは異常ではなく、避けるべき正常系の分岐として扱います。
実際に分散して分かったこと
自分のスケジュールを組み直して、いくつか腑に落ちたことがあります。
- 本数を減らさなくても、時刻を散らすだけでピークは大きく緩みます。私は重い処理の同時刻集中を解いただけで、まとめてこける現象がほぼ消えました。
- 総実行回数も手前で丸めておくと効きます。毎日の実行回数を数回ぶん減らし、夜の同時刻集中を分散した結果、限られた枠での取り合いが起きにくくなりました。
- 決定的なオフセットは、把握しやすさと分散を両立します。同じジョブは毎回同じ時刻なので、どれがいつ動くかを頭に入れたまま、集中だけを避けられました。
数字で言えば、時刻を散らして重なりガードを入れ、総実行回数を1割ほど手前で丸めたことで、夜間にジョブがまとめて弾かれる日がなくなりました。増やしたのは分散だけで、処理そのものは減らしていません。私自身は、本番で毎日回す処理ほど、本数を増やす前に時刻の分散を先に整えることを推奨します。同時起動という落とし穴を回避するほうが、効果が確実だったからです。
次の一歩
もし背景スケジュールに丸い時刻でジョブを並べているなら、まず自分のスケジュールを時刻順に並べて眺めてみてください。おそらく同じ時刻に処理が寄っている箇所が見つかります。そこを、本数はそのままに分単位でずらし、前の枠が動いていれば見送るガードを一枚足すだけで、深夜のピークは自分の手で解けます。
同じように複数の定期処理を背景で回している方の設計の助けになれば幸いです。