# 通知設計書 61-WaitForFirstConsumer

## 概要

本ドキュメントは、KubernetesにおけるPersistentVolumeClaim（PVC）の遅延バインディングモード時に、最初の利用者（Consumer）を待機していることをユーザーに通知する「WaitForFirstConsumer」イベントの設計を記述する。

### 本通知の処理概要

PVCが`WaitForFirstConsumer`（遅延バインディング）モードのStorageClassを使用している場合、PVの割り当てはPodが実際にスケジュールされるまで遅延される。この通知はその待機状態をユーザーに伝えるために発行される。

**業務上の目的・背景**：StorageClassのvolumeBindingModeが`WaitForFirstConsumer`に設定されている場合、PVCは即座にPVにバインドされず、最初にそのPVCを参照するPodがノードにスケジュールされるまで待機する。この遅延バインディングにより、ボリュームがPodと同じトポロジ（ゾーン、ノード）に確実に割り当てられるが、ユーザーから見るとPVCが「Pending」状態のままであるため、正常な動作であることを通知する必要がある。

**通知の送信タイミング**：PVコントローラがunbound状態のPVCを同期（syncUnboundClaim）する際、遅延バインディングモードのPVCに対して、まだ参照するPodが存在しない場合に発行される。

**通知の受信者**：PVCオブジェクトに対するKubernetes Eventとして記録され、kubectl describe pvc等で確認可能。クラスタ管理者およびPVCを作成した開発者が対象。

**通知内容の概要**：「waiting for first consumer to be created before binding」というメッセージが、PVCオブジェクトに対するNormalイベントとして記録される。

**期待されるアクション**：ユーザーはPVCを参照するPodを作成してデプロイする必要がある。Podがスケジュールされれば、PVCのバインディングが開始される。通知自体は正常な待機状態を示すため、エラー対応は不要。

## 通知種別

Kubernetes Event（EventType: Normal）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（PVコントローラの同期ループ内で発行） |
| 優先度 | 低（情報通知） |
| リトライ | なし（同期ループで繰り返し発行される） |

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

PVCオブジェクトに対するEventとして記録される。送信先はPVCオブジェクト自体であり、eventRecorder.Event()のターゲットとしてclaim（PVC）が指定される。

## 通知テンプレート

### メール通知の場合

本通知はKubernetes Eventとして発行されるため、メール送信は行わない。

### 本文テンプレート

```
waiting for first consumer to be created before binding
```

### 添付ファイル

なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| なし | 固定メッセージのため変数なし | - | - |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| コントローラ同期 | PVコントローラによるPVC同期処理 | PVCが遅延バインディングモードかつPodが参照していない | emitEventForUnboundDelayBindingClaimメソッド内で判定 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| PVCを参照するPodが存在する | Podが存在する場合はWaitForPodScheduledイベントに切り替わる |
| PVCが既にバインド済み | バインド済みのPVCに対しては同期処理でこのパスに到達しない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[PVコントローラ同期ループ] --> B[syncUnboundClaim]
    B --> C{遅延バインディングモード?}
    C -->|No| D[通常バインディング処理]
    C -->|Yes| E[emitEventForUnboundDelayBindingClaim]
    E --> F[findNonScheduledPodsByPVC]
    F --> G{参照するPodが存在?}
    G -->|No| H[WaitForFirstConsumer イベント発行]
    G -->|Yes| I[WaitForPodScheduled イベント発行]
    H --> J[終了]
    I --> J
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| PersistentVolumeClaim | 対象PVCの状態確認 | API Server経由 |
| StorageClass | volumeBindingModeの確認 | classLister経由 |
| Pod | PVCを参照するPodの検索 | findNonScheduledPodsByPVC |

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

#### PersistentVolumeClaim

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| spec.volumeName | バインド状態の確認 | 空文字列であること |
| spec.storageClassName | StorageClassの参照 | 遅延バインディングモードの判定 |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| Event | INSERT | Kubernetes Eventオブジェクトの作成 |

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

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| INSERT | reason | WaitForFirstConsumer | イベント理由 |
| INSERT | message | waiting for first consumer to be created before binding | イベントメッセージ |
| INSERT | type | Normal | イベントタイプ |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| Pod検索失敗 | findNonScheduledPodsByPVCがエラーを返した場合 | エラーを返し、次の同期ループで再試行 |
| StorageClass取得失敗 | classListerからStorageClassを取得できない場合 | エラーを返し、上位で処理 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | PVコントローラの同期ループにより自動的に再試行 |
| リトライ間隔 | PVコントローラの同期間隔に依存 |
| リトライ対象エラー | Pod検索エラー |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | Kubernetes Eventのデフォルトレート制限に従う |
| 1日あたり上限 | 制限なし（Eventの自動集約による） |

### 配信時間帯

制限なし（コントローラの動作タイミングに依存）

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

本通知は固定メッセージであり、機密情報を含まない。PVC名やNamespaceはEventオブジェクトのメタデータとして記録されるが、これはKubernetesの標準的な動作である。

## 備考

- WaitForFirstConsumerはWaitForPodScheduled（No.62）と密接に関連しており、同一メソッド（emitEventForUnboundDelayBindingClaim）内で条件分岐により使い分けられる
- StorageClassのvolumeBindingModeがWaitForFirstConsumerの場合のみ発行される

---

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

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

### 推奨読解順序

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

イベント定数の定義と、PVC/StorageClassの関係を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | event.go | `pkg/controller/volume/events/event.go` | WaitForFirstConsumer定数の定義（行32） |
| 1-2 | types.go | `staging/src/k8s.io/api/storage/v1/types.go` | VolumeBindingMode型とWaitForFirstConsumer値の定義 |

**読解のコツ**: イベント理由（Reason）は文字列定数として定義されており、コントローラ内でeventRecorder.Event()に渡される。

#### Step 2: エントリーポイントを理解する

PVコントローラのPVC同期処理がエントリーポイントとなる。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | pv_controller.go | `pkg/controller/volume/persistentvolume/pv_controller.go` | syncUnboundClaimメソッド（行329付近）がエントリーポイント |

**主要処理フロー**:
1. **行335-340**: 遅延バインディングモードかどうかをIsDelayBindingModeで判定
2. **行306-327**: emitEventForUnboundDelayBindingClaimで条件に応じたイベントを発行

#### Step 3: イベント発行ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | pv_controller.go | `pkg/controller/volume/persistentvolume/pv_controller.go` | emitEventForUnboundDelayBindingClaimメソッド（行304-327） |

**主要処理フロー**:
- **行307**: デフォルトでreason = WaitForFirstConsumerを設定
- **行308**: デフォルトメッセージを設定
- **行309**: findNonScheduledPodsByPVCでPodを検索
- **行313-323**: Podが存在する場合はWaitForPodScheduledに切り替え
- **行325**: eventRecorder.Eventでイベント発行

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

```
PersistentVolumeController.syncUnboundClaim
    |
    +-- storagehelpers.IsDelayBindingMode (遅延バインディング判定)
    |
    +-- emitEventForUnboundDelayBindingClaim
           |
           +-- findNonScheduledPodsByPVC (Pod検索)
           |
           +-- eventRecorder.Event (イベント発行)
```

### データフロー図

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

PVC (Pending状態)  ───> PVコントローラ同期ループ          ───> Kubernetes Event
StorageClass       ───> IsDelayBindingMode判定           (WaitForFirstConsumer)
                   ───> emitEventForUnboundDelayBindingClaim
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| event.go | `pkg/controller/volume/events/event.go` | ソース | イベント理由定数の定義 |
| pv_controller.go | `pkg/controller/volume/persistentvolume/pv_controller.go` | ソース | PVコントローラ本体、イベント発行ロジック |
| pv_helpers.go | `staging/src/k8s.io/component-helpers/storage/volume/pv_helpers.go` | ソース | IsDelayBindingMode関数 |
| types.go | `staging/src/k8s.io/api/storage/v1/types.go` | ソース | StorageClass型定義 |
