# 通知設計書 99-InvalidSchedule

## 概要

本ドキュメントは、CronJobのスケジュール表現が論理的に無効な際に発行されるKubernetesイベント通知「InvalidSchedule」の設計を記述する。

### 本通知の処理概要

CronJobのSpec.Scheduleが構文的にはパース可能だが、論理的に不正なスケジュール（例：2月31日など）である場合にWarningイベントとして発行される通知である。

**業務上の目的・背景**：Cronスケジュール表現は構文的に正しくても、論理的に実行不可能な場合がある。例えば「59 23 31 2 *」（2月31日23時59分）は構文的には正しいが、2月31日は存在しないため、2つのスケジュール間の時間差が0秒以下になりエラーとなる。本通知はこのような論理エラーを検知し、CronJobの設定修正を促す。

**通知の送信タイミング**：`syncCronJob`メソッド内で`nextScheduleTime`関数がエラーを返した場合に発行される（行533）。`nextScheduleTime`は内部で`mostRecentScheduleTime`を呼び出し、2つの連続するスケジュール時刻の差が1秒未満の場合にエラーを返す。

**通知の受信者**：Kubernetesイベントとして該当CronJobオブジェクトに記録される。

**通知内容の概要**：EventTypeはWarning、Reasonは「InvalidSchedule」、メッセージにはスケジュール表現とエラー詳細が含まれる。フォーマットは「invalid schedule: {スケジュール表現} : {エラー内容}」である。

**期待されるアクション**：受信者はCronJobのSpec.Scheduleを論理的に有効な値に修正する。具体的には、存在しない日付の組み合わせや、実行不可能なスケジュール間隔を避ける。

## 通知種別

Kubernetesイベント（Event API リソース）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（syncCronJob処理内で即座に発行） |
| 優先度 | 高（CronJobが正常に動作しない） |
| リトライ | CronJobのSpec.Scheduleが更新されるまで再調停されない |

### 送信先決定ロジック

CronJobコントローラの`recorder.Eventf`メソッドにより、対象のCronJobオブジェクトに対してイベントが記録される。

## 通知テンプレート

### メール通知の場合

本通知はKubernetesイベントであり、メール送信は行わない。

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | N/A（Kubernetesイベント） |
| 送信元名称 | cronjob-controller |
| 件名 | N/A |
| 形式 | Kubernetesイベントリソース |

### 本文テンプレート

```
invalid schedule: %s : %s
```

第1変数`%s`にはスケジュール表現（`cronJob.Spec.Schedule`）、第2変数`%s`にはエラー内容が挿入される。

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| N/A | N/A | N/A | 添付ファイルなし |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| cronJob.Spec.Schedule | CronJobのスケジュール表現 | CronJob.Spec.Schedule | Yes |
| err | エラー内容 | nextScheduleTime戻り値 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| コントローラ同期 | CronJob同期処理 | nextScheduleTimeがエラーを返した場合 | syncCronJob内のスケジュール計算 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| スケジュール計算成功 | nextScheduleTimeが正常に値を返した場合 |
| パース失敗 | スケジュール解析自体が失敗した場合はUnparseableScheduleが先に発行される |
| CronJobがSuspend中 | スケジュール計算に到達しない |
| タイムゾーン不正 | UnknownTimeZoneが先に発行される |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[syncCronJob スケジュール処理] --> B[ParseCronSchedule]
    B -->|パース失敗| C[UnparseableScheduleイベント]
    B -->|パース成功| D[nextScheduleTime呼出]
    D --> E{エラー発生?}
    E -->|エラーあり| F[InvalidScheduleイベント発行]
    F --> G[nil返却（リキューなし）]
    E -->|エラーなし| H{scheduledTimeあり?}
    H -->|なし| I[次回スケジュールにリキュー]
    H -->|あり| J[Job作成処理へ]
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| CronJob | スケジュール情報取得 | Informerキャッシュ経由 |

### テーブル別参照項目詳細

#### CronJob

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| Spec.Schedule | スケジュール計算対象 | syncCronJob内 |
| Spec.StartingDeadlineSeconds | mostRecentScheduleTime内で使用 | syncCronJob内 |
| Status.LastScheduleTime | スケジュール計算の基準時刻 | mostRecentScheduleTime内 |
| ObjectMeta.CreationTimestamp | スケジュール計算の基準時刻（代替） | mostRecentScheduleTime内 |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| Event | INSERT | InvalidScheduleイベント作成 |

#### 送信ログテーブル

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| INSERT | Event.Type | Warning | イベント種別 |
| INSERT | Event.Reason | InvalidSchedule | イベント理由 |
| INSERT | Event.Message | invalid schedule: {schedule} : {err} | スケジュールエラー |
| INSERT | Event.Source.Component | cronjob-controller | 発行元コンポーネント |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 論理エラー | 2つのスケジュール間隔が1秒未満 | Spec.Scheduleを実行可能なスケジュールに修正 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 自動リトライなし。Spec.Schedule更新により再キューされる |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | Kubernetesイベントのデフォルト集約ルールに従う |
| 1日あたり上限 | 特に制限なし |

### 配信時間帯

制限なし。

## セキュリティ考慮事項

スケジュール表現がメッセージに含まれるが、機密情報は通常含まれない。

## 備考

- InvalidScheduleとUnparseableScheduleの違い：UnparseableScheduleはCron表現の構文エラー、InvalidScheduleは構文は正しいが論理的に不正なスケジュール
- エラーメッセージの例：「time difference between two schedules is less than 1 second」（utils.go 行130）
- nextScheduleTimeからのエラーは、mostRecentScheduleTime内で2つのスケジュール時刻差が1秒未満の場合に発生する（utils.go 行128-131）

---

## コードリーディングガイド

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | types.go | `staging/src/k8s.io/api/batch/v1/types.go` | CronJob.Spec.Scheduleの仕様を理解する |

**読解のコツ**: Cronスケジュールは構文的に正しくても論理的に不正な場合がある（2月31日等）。

#### Step 2: mostRecentScheduleTimeのエラー条件を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | utils.go | `pkg/controller/cronjob/utils.go` | mostRecentScheduleTime関数（行100-181）、特に行128-131のスケジュール間隔チェックを理解する |

**主要処理フロー**:
1. **行115-116**: 次の2つのスケジュール時刻t1, t2を計算
2. **行128**: t2 - t1の差を秒単位で計算
3. **行129-131**: 差が1秒未満の場合エラー返却「time difference between two schedules is less than 1 second」

#### Step 3: nextScheduleTimeのエラー伝搬を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | utils.go | `pkg/controller/cronjob/utils.go` | nextScheduleTime関数（行210-223）でmostRecentScheduleTimeのエラーがどのように返されるかを理解する |
| 3-2 | cronjob_controllerv2.go | `pkg/controller/cronjob/cronjob_controllerv2.go` | syncCronJob内のnextScheduleTime呼出とエラーハンドリング（行528-535）を理解する |

**主要処理フロー**:
- **行528**: nextScheduleTime呼出
- **行529-535**: エラー発生時 -> InvalidScheduleイベント発行 + nil返却

### プログラム呼び出し階層図

```
ControllerV2.syncCronJob (行426)
    |
    +-- parsers.ParseCronScheduleWithPanicRecovery (行519)
    +-- nextScheduleTime (行528)
         |
         +-- mostRecentScheduleTime (行211, 100)
              |
              +-- schedule.Next(earliestTime) -> t1 (行115)
              +-- schedule.Next(t1) -> t2 (行116)
              +-- t2 - t1 < 1s ? -> error (行128-131)
         |
         +-- [エラーあり]
              +-- recorder.Eventf "InvalidSchedule" (行533) ★本通知
              +-- nil返却 (行534)
```

### データフロー図

```
[入力]                              [処理]                         [出力]

CronJob.Spec.Schedule         --> nextScheduleTime            --> InvalidSchedule Event
CronJob.Status.LastScheduleTime    |                               (Warning)
CronJob.CreationTimestamp          +-- mostRecentScheduleTime
                                   |    +-- schedule.Next
                                   |    +-- 時刻差チェック
                                   +-- recorder.Eventf
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| cronjob_controllerv2.go | `pkg/controller/cronjob/cronjob_controllerv2.go` | ソース | InvalidScheduleイベント発行（行533） |
| utils.go | `pkg/controller/cronjob/utils.go` | ソース | mostRecentScheduleTime（行100-181）でスケジュール間隔チェック、nextScheduleTime（行210-223）でエラー伝搬 |
