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?

cron 監視は「失敗検知」と「N 時間記録なし検知」の 2 段構え — 沈黙の死を見逃さない

0
Posted at

この記事は約 5 分で読めます。

筆者プロフィール: ソフトウェアエンジニア。「知った気にならない。いつまでも学び続ける」を信条に、業務と個人開発の両輪で技術を磨いています。AI 駆動開発で複数の個人開発アプリを構築・運用中。
👉 ポートフォリオ: 筆者ホームページ

cron 監視を「失敗検知」だけで済ませると、「cron 自体が登録されていない / 削除された / サーバが死んだ」 ケースで何も検知できません。本記事では、運用中の SaaS 「たすきば Knowledge Relay」 で採用した 2 段構え監視 を整理します。

サービスの機能紹介・画面イメージ・コンセプトは公式プロダクトページをご覧ください。
👉 たすきば Knowledge Relay — 公式プロダクトページ

失敗検知だけの落とし穴

通常、cron 監視と聞くと:

cron 実行 → 失敗したら通知

という設計を考えます。しかし、これだと 致命的な盲点 があります。

実例シナリオ

1. cron-job.org に登録した cron が、何らかの理由で削除された (アカウントロック等)
2. cron が一切実行されない
3. 失敗ステータスすら記録されない (= 通知も来ない)
4. 月末になって「月次集計が動いていなかった」と気づく

これはたすきばで実際に踏んだ罠。failure 検知では何も気づけませんでした


1. 解決 — 2 段構え監視

段 1: failure 検知

cron が実行されて失敗を返したら検知。

// cron 実行のたびに記録
await prisma.cronExecutionLog.create({
  data: {
    cronName: 'monthly-billing-report',
    startedAt,
    finishedAt: new Date(),
    status: success ? 'success' : 'failure',
    errorMessage: success ? null : error.message,
  },
});

// 監視 cron で failure を検知
const failures = await prisma.cronExecutionLog.findMany({
  where: { status: 'failure', detectedAt: null },
});
if (failures.length > 0) {
  await notifySuperAdmin({ type: 'cron_failure', details: failures });
}

段 2: 「N 時間記録なし」検知

cron が一切実行されていない場合を検知。

const expectedCrons = [
  { name: 'daily-drift-check', maxIntervalHours: 24 },
  { name: 'monthly-billing-report', maxIntervalHours: 24 * 31 },
  { name: 'warm-up-ping', maxIntervalHours: 1 },
];

for (const cron of expectedCrons) {
  const lastExecution = await prisma.cronExecutionLog.findFirst({
    where: { cronName: cron.name },
    orderBy: { finishedAt: 'desc' },
  });

  const hoursAgo = lastExecution
    ? (Date.now() - lastExecution.finishedAt.getTime()) / 3600000
    : Infinity;

  if (hoursAgo > cron.maxIntervalHours) {
    await notifySuperAdmin({
      type: 'cron_silence',
      cronName: cron.name,
      lastSeenHoursAgo: hoursAgo,
    });
  }
}

「期待される実行頻度」を maxIntervalHours として定義し、それを超えたら死亡疑いとして通知 します。


2. 監視 cron 自体が死んだら?

ここでメタな問題が発生します。

監視 cron 自体が死んだら、どうやって検知するか?

たすきばの答え:

1. 監視 cron は最も信頼性の高いプラットフォーム (cron-job.org) で稼働
2. 監視 cron は別 endpoint で死活監視 (GitHub Actions schedule で 1 日 1 回 ping)
3. GitHub Actions も死んだら? → そこは諦める (現実的な妥協点)

完全な死活監視は無理。ただし、段数を増やすほど死亡確率は指数的に下がる ので、3 層あれば十分。


3. expectedCrons リストを Code で管理

監視対象の cron 一覧は、コードで管理します。

// src/config/cron-watchdog.ts
export const EXPECTED_CRONS = [
  {
    name: 'daily-drift-check',
    maxIntervalHours: 24,
    description: '日次 drift 検知',
  },
  {
    name: 'monthly-billing-report',
    maxIntervalHours: 24 * 31,
    description: '月次請求レポート',
  },
  {
    name: 'embedding-backfill',
    maxIntervalHours: 24 * 31,
    description: '月初 embedding 補完',
  },
];
運用 内容
新規 cron 追加時 この配列に登録
登録漏れ 「監視されない cron」になる
PR レビュー 必須確認項目にする

4. 通知を Discord + Email で多層化

「通知が出ても誰も見ていない」状態を防ぐため、通知は Discord と Email の 両方 に送ります。

async function notifySuperAdmin(payload: NotificationPayload) {
  await Promise.all([
    notifyDiscord(payload),
    notifyEmail(payload, process.env.SUPER_ADMIN_EMAIL!),
  ]);
}
シナリオ フォロー
Discord を見ていないとき メールで気づく
メールが埋もれたとき Discord で気づく

個人開発で「通知を見落とす」リスクを減らす 多層防御 です。


5. CronExecutionLog のスキーマ

cron 実行ログには cronName を必須カラムにします。

model CronExecutionLog {
  id            String   @id @default(uuid())
  cronName      String   // 必須
  startedAt     DateTime
  finishedAt    DateTime
  status        String   // 'success' | 'failure'
  errorMessage  String?
  detectedAt    DateTime?

  @@index([cronName, finishedAt])
}

これにより:

  • ある cron がいつ最後に動いたか即座に分かる
  • 監視テストで「この cron が今日 1 回動いたか」を verify できる

監視を仕組み化するには、データ構造から逆算 します。


6. 横展開できる場面

このパターンは cron に限らず、

対象 監視内容
バッチ処理 期待される実行頻度を超えたら通知
定期メール送信 N 時間以上送信記録がなければ通知
外部 API ping 連続失敗 + 「全く動いていない」を別々に監視

「定期的に何かが起きるはず」 の処理に広く適用できます。


おわりに — 失敗検知と無音検知は別物

「失敗検知」と「無音検知」は別物。

サービスを運用するときは、

カテゴリ 意味
失敗 動いたが結果が NG
無音 そもそも動かなかった

の 2 つを区別して監視する必要があります。

「動かなかった」を検知するには、「何時間以内に動くべきか」を事前に定義し、それを超えたら通知する仕組み が必要です。

仕組み 効果
failure 検知 (cron 実行ログ) 実行されて失敗したケースを検知
「N 時間記録なし」検知 cron 自体が止まったケースを検知
expectedCrons リストで Code 管理 新規 cron の登録漏れを防ぐ
通知を Discord + Email 見落とし防止

個人開発で「気づいたら何ヶ月も動いていなかった」を防ぐための、最小限の防御策 です。

本記事の cron 監視は、運用中の SaaS 「たすきば Knowledge Relay」 で実装しています。
👉 たすきば Knowledge Relay — 公式プロダクトページ

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?