# 通知設計書 3-TimeoutClusterStateListener通知

## 概要

本ドキュメントは、OpenSearchにおけるTimeoutClusterStateListener通知の設計仕様を記載する。TimeoutClusterStateListenerはClusterStateListenerを拡張し、タイムアウト付きでクラスタ状態変更を監視する機能を追加したインターフェースである。指定時間内にクラスタ状態が期待する条件を満たさない場合にonTimeout()で通知する。

### 本通知の処理概要

TimeoutClusterStateListener通知は、クラスタ状態変更をタイムアウト付きで監視し、時間内に期待する状態変化が発生しなかった場合にタイムアウトを通知する仕組みである。

**業務上の目的・背景**：分散システムにおいて、特定のクラスタ状態変化を待つ操作にはデッドロック防止のためのタイムアウトが不可欠である。例えば、スナップショットのリストア完了待ちやインデックスの緑状態への遷移待ちなど、特定の状態変化を一定時間以内に検出する必要があるユースケースで利用される。ClusterStateObserverが内部的にこのインターフェースを使用し、条件付きのクラスタ状態待ちを実現している。

**通知の送信タイミング**：(1) リスナー登録直後にpostAdded()が呼ばれ、初期化処理を行う。(2) クラスタ状態が変化するたびにclusterChanged()（ClusterStateListenerから継承）が呼ばれる。(3) 指定タイムアウトが経過した場合にonTimeout()が呼ばれる。(4) ClusterApplierServiceが閉じられた場合にonClose()が呼ばれる。

**通知の受信者**：TimeoutClusterStateListenerインターフェースを実装し、ClusterApplierService#addTimeoutListener()で登録されたコンポーネント。代表的にはClusterStateObserverの内部クラスObserverClusterStateListenerがある。

**通知内容の概要**：clusterChanged()ではClusterChangedEventが渡される。onTimeout()ではTimeValueオブジェクト（タイムアウト時間）が渡される。postAdded()とonClose()はパラメータなし。

**期待されるアクション**：受信者はclusterChanged()で期待する状態変化が発生したかを判定し、条件を満たせば処理を続行する。onTimeout()ではタイムアウトエラーを呼び出し元に伝搬する。onClose()ではリソースのクリーンアップを行う。

## 通知種別

プロセス内コールバック通知（Javaインターフェースによるリスナーパターン）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（クラスタ適用スレッド上で実行。タイムアウトはスケジューラスレッドで発火） |
| 優先度 | 高（HIGH優先度でイベントキューに投入） |
| リトライ | 無し |

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

ClusterApplierService#addTimeoutListener(TimeValue timeout, TimeoutClusterStateListener listener)で登録された個別のリスナーに対して通知する。timeoutClusterStateListenersマップ（ConcurrentHashMap）で管理される。

## 通知テンプレート

### メール通知の場合

本通知はプロセス内コールバックであるため、メール通知は該当しない。

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | N/A |
| 送信元名称 | N/A |
| 件名 | N/A |
| 形式 | N/A |

### 本文テンプレート

```
N/A（Javaメソッドコールバックとして通知）
```

### 添付ファイル

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

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| event | クラスタ状態変更イベント | ClusterChangedEvent | clusterChanged時 |
| timeout | タイムアウト時間 | TimeValue | onTimeout時 |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| リスナー登録 | addTimeoutListener()呼び出し | リスナーが登録されること | postAdded()が呼ばれる |
| クラスタ状態変化 | ClusterState更新 | ClusterApplierServiceでの状態適用 | clusterChanged(event)が呼ばれる |
| タイムアウト | 指定時間経過 | タイムアウト時間内に期待する状態変化が未発生 | onTimeout(timeout)が呼ばれる |
| サービス終了 | ClusterApplierService停止 | ライフサイクルが停止状態 | onClose()が呼ばれる |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| タイムアウトがnull | タイムアウトが指定されていない場合、onTimeout()は呼ばれない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[addTimeoutListener呼び出し] --> B[NotifyTimeoutオブジェクト生成]
    B --> C[timeoutClusterStateListenersに登録]
    C --> D[listener.postAdded 呼び出し]
    D --> E{タイムアウト指定あり?}
    E -->|Yes| F[スケジューラにタイムアウトタスク登録]
    E -->|No| G[タイムアウトなしで待機]
    F --> H{先に発生するのは?}
    G --> H
    H -->|クラスタ状態変化| I[listener.clusterChanged]
    H -->|タイムアウト| J[listener.onTimeout]
    H -->|サービス停止| K[listener.onClose]
    I --> L[終了]
    J --> L
    K --> L
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| N/A | N/A | RDBは使用しない |

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

N/A

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| N/A | N/A | RDBは使用しない |

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

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| N/A | N/A | N/A | N/A |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| タイムアウト | 指定時間内にクラスタ状態が期待する条件を満たさない | onTimeout()コールバックで通知。呼び出し元がProcessClusterEventTimeoutExceptionをスロー |
| サービス停止 | ClusterApplierServiceが停止した | onClose()コールバックで通知 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし

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

プロセス内通知であるため、ネットワーク上のセキュリティリスクは限定的。

## 備考

- @PublicApi(since = "1.0.0")として公開APIに指定されている
- ClusterStateListenerを拡張しており、clusterChanged()メソッドも利用可能
- ClusterStateObserverが主要な利用者であり、条件付きクラスタ状態待ちを実現する
- タイムアウトリスナーはリスナー自身が解除する責任を持つ（自動解除はされない）
- ClusterApplierServiceが停止済みの場合、addTimeoutListener()呼び出し時に即座にonClose()が呼ばれる

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | TimeoutClusterStateListener.java | `server/src/main/java/org/opensearch/cluster/TimeoutClusterStateListener.java` | ClusterStateListenerを拡張し、postAdded/onClose/onTimeoutの3メソッドを追加（行44-51） |
| 1-2 | ClusterStateListener.java | `server/src/main/java/org/opensearch/cluster/ClusterStateListener.java` | 親インターフェースのclusterChanged()（行48） |

**読解のコツ**: TimeoutClusterStateListenerはClusterStateListenerを拡張しているため、合計4つのコールバック（clusterChanged, postAdded, onClose, onTimeout）を持つ。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ClusterApplierService.java | `server/src/main/java/org/opensearch/cluster/service/ClusterApplierService.java` | addTimeoutListener()メソッド（行324-341）でリスナーの登録とタイムアウトスケジューリングを確認 |

**主要処理フロー**:
1. **行324**: `addTimeoutListener(@Nullable TimeValue timeout, TimeoutClusterStateListener listener)` - タイムアウト付きリスナー登録
2. **行334-335**: NotifyTimeoutオブジェクトを生成しtimeoutClusterStateListenersに登録
3. **行331**: HIGH優先度でイベントキューに投入
4. **行672**: callClusterStateListeners内でtimeoutClusterStateListeners.keySet()に対してもclusterChanged()が呼ばれる

#### Step 3: ClusterStateObserverの利用パターンを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ClusterStateObserver.java | `server/src/main/java/org/opensearch/cluster/ClusterStateObserver.java` | ObserverClusterStateListenerクラス（行68）でTimeoutClusterStateListenerを実装している |

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

```
ClusterApplierService#addTimeoutListener()
    |
    +-- NotifyTimeout生成
    |       +-- タイムアウトスケジューラ登録
    |
    +-- listener.postAdded()
    |
    +-- [クラスタ状態変化時]
    |       +-- callClusterStateListeners()
    |               +-- listener.clusterChanged(event)
    |
    +-- [タイムアウト時]
    |       +-- NotifyTimeout#run()
    |               +-- listener.onTimeout(timeout)
    |
    +-- [サービス停止時]
            +-- listener.onClose()
```

### データフロー図

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

リスナー登録   ───▶ ClusterApplierService  ───▶ postAdded()
ClusterState変化 ───▶ callClusterStateListeners ───▶ clusterChanged(event)
タイムアウト発火 ───▶ NotifyTimeout#run()      ───▶ onTimeout(timeout)
サービス停止   ───▶ lifecycle check          ───▶ onClose()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| TimeoutClusterStateListener.java | `server/src/main/java/org/opensearch/cluster/TimeoutClusterStateListener.java` | ソース | リスナーインターフェース定義 |
| ClusterStateListener.java | `server/src/main/java/org/opensearch/cluster/ClusterStateListener.java` | ソース | 親インターフェース |
| ClusterApplierService.java | `server/src/main/java/org/opensearch/cluster/service/ClusterApplierService.java` | ソース | リスナー管理とタイムアウトスケジューリング |
| ClusterStateObserver.java | `server/src/main/java/org/opensearch/cluster/ClusterStateObserver.java` | ソース | 主要な利用者（条件付きクラスタ状態待ち） |
