# 機能設計書 48-GangSchedulingプラグイン

## 概要

本ドキュメントは、Kubernetesスケジューラーの`GangScheduling`プラグインの機能設計を記述する。本プラグインはWorkload APIと連携し、ギャングスケジューリング（"all-or-nothing"スケジューリング）を実現する。同一Workloadに属するPodグループが最小数（MinCount）に達するまでスケジューリングを待機し、条件を満たしたら一斉に配置を許可する。

### 本機能の処理概要

**業務上の目的・背景**：分散機械学習やHPC（High Performance Computing）ワークロードでは、全ワーカーPodが同時に利用可能でなければ処理を開始できない場合がある。ギャングスケジューリングにより、MinCount数のPodが全てスケジュール可能になるまで待機し、一括で配置を確定する。

**機能の利用シーン**：PodにWorkloadRefが設定され、対応するWorkloadオブジェクトにGangスケジューリングポリシーが定義されている場合に動作する。

**主要な処理内容**：
1. PreEnqueue: Workloadの存在確認、PodGroupのGangポリシー確認、MinCount数のPodが存在するかを検証
2. Reserve: Podの"assumed"状態をWorkloadManagerに登録
3. Permit: 全assumedまたはassigned Podの数がMinCountに達するまで待機。達した場合、待機中の全Podを一斉に許可
4. Unreserve: assumed状態の取消

**関連システム・外部連携**：Workload API（scheduling.k8s.io/v1alpha1）、WorkloadManager。

**権限による制御**：なし（Workload APIの存在自体がフィーチャーゲートで制御）。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | スケジューラー内部プラグインとして自動実行 |

## 機能種別

ギャングスケジューリング / Permit制御

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| Pod | v1.Pod | Yes | スケジュール対象Pod（WorkloadRef含む） | - |
| Workload | schedulingapi.Workload | Yes | ギャングポリシー定義（MinCount等） | - |

### 入力データソース

- Pod.Spec.WorkloadRef
- Workload API（scheduling.k8s.io/v1alpha1）
- WorkloadManager.PodGroupState

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| PreEnqueueStatus | fwk.Status | エンキュー可否 |
| PermitStatus | fwk.Status | Wait / Allow |
| PermitTimeout | time.Duration | Permit待機タイムアウト |

## 処理フロー

### 処理シーケンス

```
1. PreEnqueue: ギャングの前提条件チェック
   ├─ WorkloadRefなし → スキップ（nil返却）
   ├─ Workload取得（NotFound → UnschedulableAndUnresolvable）
   ├─ PodGroupPolicy取得（Gangポリシーなし → スキップ）
   └─ PodGroupState.AllPods() < MinCount → UnschedulableAndUnresolvable

2. Reserve: Pod仮定登録
   └─ PodGroupState.AssumePod(pod.UID)

3. Permit: ギャングクォーラムチェック
   ├─ WorkloadRefなし → スキップ
   ├─ Workload/PodGroupPolicy取得
   ├─ assumed + assigned Podの合計数 < MinCount
   │      ├─ unscheduledPodsをActivate（待機中のPodを起こす）
   │      └─ Wait状態返却（SchedulingTimeout付き）
   └─ MinCount達成
          ├─ 全assumed Podの waitingPod.Allow(Name) を呼び出し
          └─ nil返却（自身も許可）

4. Unreserve: 仮定取消
   └─ PodGroupState.ForgetPod(pod.UID)
```

### フローチャート

```mermaid
flowchart TD
    A[PreEnqueue: 前提条件チェック] --> B{WorkloadRefあり?}
    B -->|No| C[スキップ]
    B -->|Yes| D{Workload存在?}
    D -->|No| E[UnschedulableAndUnresolvable]
    D -->|Yes| F{Gangポリシーあり?}
    F -->|No| G[スキップ]
    F -->|Yes| H{AllPods >= MinCount?}
    H -->|No| I[UnschedulableAndUnresolvable]
    H -->|Yes| J[スケジューリング開始]
    J --> K[Reserve: AssumePod]
    K --> L[Permit: クォーラムチェック]
    L --> M{assumed+assigned >= MinCount?}
    M -->|No| N[Wait + Activate unscheduled]
    M -->|Yes| O[Allow all waiting Pods]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | WorkloadRefなしスキップ | WorkloadRefのないPodはギャングスケジューリング対象外 | 常時 |
| BR-02 | Workload不在待ち | Workloadオブジェクトが存在しない場合はUnschedulableAndUnresolvable | WorkloadRef設定時 |
| BR-03 | Gangポリシーなしスキップ | PodGroupにGangポリシーがない場合はスキップ | Gangポリシー未定義時 |
| BR-04 | MinCountクォーラム（PreEnqueue） | AllPodsがMinCount未満の場合はエンキュー拒否 | Gang有効時 |
| BR-05 | MinCountクォーラム（Permit） | assumed + assigned PodsがMinCount未満の場合はWait | Gang有効時 |
| BR-06 | 一括Allow | クォーラム達成時、全waiting Podを一斉にAllow | MinCount達成時 |
| BR-07 | Activate | Permitでwait中に、未スケジュールPodをActivateして起動を促進 | wait中 |
| BR-08 | SchedulingTimeout | Permit waitにはタイムアウトが設定される | PodGroupState.SchedulingTimeout() |
| BR-09 | WorkloadRef不変 | WorkloadRefはimmutableのためPod/Updateイベント不要 | 常時 |

## データベース操作仕様

本プラグインはKubernetes APIの直接更新は行わない。状態はWorkloadManager内のインメモリで管理。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| UnschedulableAndUnresolvable | Workload不在 | Workloadが見つからない | Workload作成を待つ |
| UnschedulableAndUnresolvable | PodGroup不在 | PodGroupが存在しない | 設定確認 |
| UnschedulableAndUnresolvable | MinCount未達 | PodがMinCount未満 | Pod追加を待つ |
| Wait | クォーラム未達 | assumed+assigned < MinCount | タイムアウトまで待機 |

## トランザクション仕様

Reserve-Permit-Unreserveパターン。Permitでタイムアウトした場合はUnreserveが呼ばれ、AssumePodが取り消される。

## パフォーマンス要件

- QueueingHintにより関連Pod/Workloadの追加時のみ再スケジュール
- WorkloadRef immutableによりPod/Updateイベント不要

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

- WorkloadはNamespaceスコープ
- PodのWorkloadRefとWorkloadの一致検証

## 備考

- Workload APIはv1alpha1（2025年時点）
- PodGroups定義はWorkload内でimmutable

---

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | gangscheduling.go | `pkg/scheduler/framework/plugins/gangscheduling/gangscheduling.go` | GangScheduling構造体（45-48行目）、インターフェース実装（50-53行目） |

#### Step 2: PreEnqueueを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | gangscheduling.go | 同上 | PreEnqueue（116-154行目） |

**主要処理フロー**:
- **117-119行目**: WorkloadRefなしチェック
- **124-131行目**: Workload取得（NotFoundハンドリング含む）
- **134-141行目**: PodGroupPolicy取得とGangポリシー確認
- **143-150行目**: AllPodsとMinCountの比較

#### Step 3: Reserve/Unreserveを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | gangscheduling.go | 同上 | Reserve（159-169行目）、Unreserve（173-183行目） |

**主要処理**: AssumePod/ForgetPodによるPodGroupStateの状態管理。

#### Step 4: Permitを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | gangscheduling.go | 同上 | Permit（187-237行目） |

**主要処理フロー**:
- **212-213行目**: PodGroupStateからassumedPodsとassignedPodsを取得
- **216-218行目**: assumed + assignedのUnion数とMinCountの比較
- **220-223行目**: クォーラム未達時のActivateとWait返却
- **229-233行目**: クォーラム達成時の全waiting Pod Allow

#### Step 5: QueueingHintを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | gangscheduling.go | 同上 | EventsToRegister（69-78行目）、isSchedulableAfterPodAdded（80-95行目）、isSchedulableAfterWorkloadAdded（97-112行目） |

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

```
New (初期化)
    └─ WorkloadLister設定
    │
    ├─ PreEnqueue
    │      ├─ workloadLister.Get
    │      ├─ helper.PodGroupPolicy
    │      └─ WorkloadManager().PodGroupState
    │             └─ AllPods()
    │
    ├─ Reserve
    │      └─ PodGroupState.AssumePod
    │
    ├─ Permit
    │      ├─ workloadLister.Get
    │      ├─ helper.PodGroupPolicy
    │      ├─ PodGroupState.AssumedPods()
    │      ├─ PodGroupState.AssignedPods()
    │      ├─ PodGroupState.UnscheduledPods()
    │      ├─ handle.Activate (未スケジュールPod起動)
    │      └─ handle.GetWaitingPod → Allow
    │
    └─ Unreserve
           └─ PodGroupState.ForgetPod
```

### データフロー図

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

Pod.Spec.WorkloadRef     ───▶  PreEnqueue                   ───▶  エンキュー許可/拒否
Workload API                    (Gang前提条件チェック)

Pod.UID                  ───▶  Reserve                      ───▶  assumed状態登録
                                (AssumePod)

PodGroupState            ───▶  Permit                       ───▶  Wait / Allow
(assumed+assigned数)            (クォーラムチェック)                  Allow全waiting Pod

Pod.UID                  ───▶  Unreserve                    ───▶  assumed状態取消
                                (ForgetPod)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| gangscheduling.go | `pkg/scheduler/framework/plugins/gangscheduling/gangscheduling.go` | ソース | プラグイン全機能の実装 |
| helper.go | `pkg/scheduler/framework/plugins/helper/` | ソース | PodGroupPolicy、MatchingWorkloadReference |
