コンテンツにスキップ
use-cases / on-call-shift-cron / hero
CRON · EXPIRES_AT · 通知

シフトとともに失効するオンコールエスカレーション

月曜日に expires_at を金曜日 09:00 に設定した cron エントリを POST します。ジョブは 4 日間 30 分ごとに電話を鳴らし、シフトが終わった瞬間に自分自身を削除します。次のオンコール担当が自分の分を POST します。PagerDuty スケジュールも、共有スケジューラ設定も、無効化のためのカレンダーリマインダーもありません。

cron ドキュメントを読む
use-cases / on-call-shift-cron / handoff

ミーティングなしでシフトを引き継ぐ方法

1 週間の中の 3 つの時点。cron エントリの存在はシフトを正確に追跡します — 重複なし、ギャップなし、残った crontab 行なし。

月曜 09:00 → 金曜 09:01expires_at がシフトを運ぶ
月曜 09:00

エントリを POST

schedule */30、コマンドは hoody-notifications/push/me を指し、expires_at = Friday 09:00:00Z を指定した curl を 1 回。サーバーは id e7d3 を返します。

月〜金

通知があなたの携帯に届く

30 分ごとに 4 日間、エントリが実行され hoody-notifications に通知します。あなたのデバイスのみ。チームチャンネルは静かなままです。

金曜 09:01

エントリが自分自身を削除

09:00:00Z に cron サービスが e7d3 を削除します。09:30 のティックには発火するものがありません。次のオンコール担当はすでに自分の分を POST しています。

各オンコール担当はシフト開始時に 1 回 POST します。expires_at フィールドが引き継ぎプロトコルのすべてです — cron サービスが秒単位でクリーンアップを行います。

use-cases / on-call-shift-cron / mechanism

2 つの curl — シフトごとに 1 つ

ローテーションプロトコル全体は週に 2 回の HTTP コールです。オンコール担当は月曜に POST し、金曜に list して、エントリがすでに消えていることを確認します。マージすべき共有スケジュールファイルはありません。

monday.sh · m.dossantos
POST · entry
# monday 09:00 — i'm on call until friday 09:00
curl -X POST \
  https://oncall.containers.hoody.com/users/me/entries \
  -H "Content-Type: application/json" \
  -d '["schedule":"*/30 * * * *","command":"curl -fsS hoody-notifications/push/m.dossantos","comment":"on-call wk19","expires_at":"2026-05-08T09:00:00Z"]'

# response
HTTP/1.1 201 Created
{ "id":"e7d3", "expires_at":"2026-05-08T09:00:00Z", "enabled":true }
friday.sh · m.dossantos
GET · audit
# friday 09:01 — the next on-call took over
curl GET https://oncall.containers.hoody.com/users/me/entries

HTTP/1.1 200 OK
[
  // my entry e7d3 is gone — it expired
  // at 09:00 sharp. j.okafor's new
  // entry took over at 09:00:30.
]
# no slack thread, no calendar reminder

共有スケジューラサービスは関与しません。cron エントリはエンジニアが所有しており、次のオンコール担当のセットアップは前任者のクリーンアップに依存しません。

use-cases / on-call-shift-cron / powers

この形が週から取り除く 3 つのこと

自分のライフタイムを所有するオンコールエントリは、PagerDuty 設定やカレンダーリマインダーでは止められない 3 種類のミスを止めます。

ルーティング

通知は 1 つのデバイスにだけ届く — あなたのもの

エントリのコマンドが個人の通知エンドポイントを指しているため、エスカレーションはシフト中あなたの携帯にだけルーティングされます。それだけ。深夜 3 時にチームチャンネルが誤爆することはありません。

オーナーシップ

編集する共有設定なし、マージする PR なし

全員が触る escalation_policy.yaml は存在しません。各エンジニアが自分のエントリを所有します。異なるタイムゾーンの 2 人のオンコール担当が同じファイルを編集して衝突することはありません。

クリーンアップ

引き継ぎはチャットではなくデータの中にある

金曜 09:00 になったとき「あれ、まだ受信してる?」と聞く必要はありません。エントリはすでに消えています。引き継ぎの確認は、行が 1 つ少ないリストを返す GET 1 回です。

use-cases / on-call-shift-cron / capacity

cron サービスが保証するもの

数値は Hoody Cron API によるものです。捏造ではない、本物の制限値です。

  1. 失効精度1 秒

    自動失効はシステムクロックに対して動きます。09:00:00Z の expires_at は、cron ティックが発火するのと同じ分のうちに削除されます — 5 分の清掃係ラグはありません。

  2. スケジュールあたりのフィールド5

    標準の 5 フィールド cron 表現に加えて @hourly / @daily / @weekly マクロ。シフト中に 30 分ごとに発火させるには */30 * * * * です。

  3. アイソレーション1 / ユーザー

    各システムユーザーは自分の crontab を持ちます。次のオンコール担当のエントリは自分の /users/[name]/entries の下に存在し、あなたのものに触れることはありません。

Hoody Cron API による制限: マネージドエントリは UUID と expires_at を持つ JSON CRUD、ユーザーごとに raw crontab アクセスが利用可能、ユーザーごとの crontab 分離は組み込みです。

use-cases / on-call-shift-cron / punchline

シフトが終われば、cron エントリも終わります — 自動的に。

before · ローテーションファイルafter · 週 2 回の curl
古い引き継ぎはこんな感じだったoncall.yaml を編集 → PR → マージ → 誰かを呼んで無効化4 人 · 共有ファイル 1 つ · それでも金曜 11 時に通知が来た
今はこう見えるPOST /entries [ expires_at: '2026-05-08T09:00:00Z' ]エンジニア 1 人 · curl 1 回 · cron サービスがクリーンアップする
cron ドキュメントを読む
use-cases / on-call-shift-cron / replaces

これが置き換えるもの

オンコールローテーションが必要なときに手が伸びる標準的なツール。それぞれがサービス料、設定リポジトリ、引き継ぎの儀式を要求します。expires_at 付きの cron エントリが要求するのは POST 1 回だけです。

  • PagerDuty エスカレーションポリシーあなたの携帯への curl にすぎないものに、シート単位の課金
  • Opsgenie スケジュールローテーション今週誰に通知するかを決めるためだけの製品全体
  • カスタムオンコール設定リポジトリ全員が PR を出し、誰も所有しない共有 YAML
  • 「引き継ぐときに cron を無効化するのを覚えておく」設定して、無視して、金曜にもう一度送るカレンダーリマインダー
  • 手動エスカレーションクリーンアップ金曜 11 時の Slack スレッド — 「誰か購読解除してくれない?」
  • VictorOps ローテーション1 つの expires_at フィールドが行うことのために、もう 1 つのスケジューラサービス
use-cases / on-call-shift-cron / cta

金曜の朝に crontab 行を無効化するのをやめましょう。月曜に expires_at を設定して、忘れてください。

cron ドキュメントを読む
use-cases / on-call-shift-cron / related

他のユースケースを読む