# バッチ設計書 18-InternalClusterInfoService

## 概要

本ドキュメントは、クラスタ全体のディスク使用量・シャードサイズ・リソース使用統計を定期的に収集するバッチ処理「InternalClusterInfoService」の設計仕様を記載する。

### 本バッチの処理概要

InternalClusterInfoServiceは、クラスタマネージャノードでのみ動作し、全データノードのディスク使用量、シャードサイズ、ファイルキャッシュ統計、リソース使用統計を定期的に収集してClusterInfoオブジェクトとして提供するバッチ処理である。

**業務上の目的・背景**：OpenSearchのシャードアロケーションにおいて、各ノードのディスク使用量やシャードサイズは重要な判断材料である。特にディスク閾値ベースのアロケーション（DiskThresholdDecider）は、ノードのディスク使用率が閾値を超えた場合にシャードの再配置を行う。本バッチは、これらの判断に必要なクラスタ全体の情報を定期的に収集し、最新の状態を維持する。また、リソース使用統計はコーディネータノードの検索ルーティング最適化にも使用される。

**バッチの実行タイミング**：`cluster.info.update.interval`設定に基づく定期実行（デフォルト30秒間隔、最小10秒）。クラスタマネージャ選出時にスケジューリングが開始される。

**主要な処理内容**：
1. NodesStatsリクエストで全データノードのFS統計、ファイルキャッシュ統計、リソース使用統計を取得
2. IndicesStatsリクエストで全インデックスのシャードサイズと予約容量を取得
3. ディスク使用量（最小空き/最大空き）をノード別に算出
4. シャードサイズとデータパスのマッピングを構築
5. リソース使用統計をノード別に算出
6. ClusterInfoオブジェクトを更新し、リスナーに通知

**前後の処理との関連**：ResourceUsageCollectorService（No.16）が各ノードのローカルリソース統計を収集し、本バッチがNodesStatsレスポンスの一部としてそれを集約する。収集されたClusterInfoはDiskThresholdDecider、BalancedShardsAllocator等で使用される。ClusterStateListenerを実装しており、クラスタマネージャ選出やデータノード追加時にも即座にリフレッシュを実行する。

**影響範囲**：シャードアロケーション判断、ディスク閾値ベースのアロケーション、リソース使用統計に影響。クラスタマネージャノードでのみ動作するため、全ノードに影響はない。

## バッチ種別

クラスタ管理 / 統計収集

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 定期実行（デフォルト30秒間隔） |
| 実行時刻 | クラスタマネージャ選出後、設定間隔ごとに繰り返し実行 |
| 実行曜日 | 該当なし（常時） |
| 実行日 | 該当なし（常時） |
| トリガー | ThreadPool.scheduleUnlessShuttingDown（MANAGEMENTスレッドプール） |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| ローカルノードがクラスタマネージャであること | クラスタマネージャ選出時にスケジュール開始 |
| ディスク閾値ベースのアロケーションが有効であること | enabledフラグがtrue |
| データノードが2台以上存在すること | 1台以下の場合はリフレッシュをスキップ |

### 実行可否判定

clusterChanged()イベントで、localNodeClusterManager()==trueの場合にRefreshAndRescheduleRunnableを作成してスケジュール開始。クラスタマネージャでなくなった場合はrefreshAndRescheduleRunnableをnullに設定し停止。enabled==falseの場合はRefreshRunnable.doRun()でスキップ。

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| cluster.info.update.interval | TimeValue | No | 30s | 更新間隔（最小10秒） |
| cluster.info.update.timeout | TimeValue | No | 15s | 各統計リクエストのタイムアウト |
| cluster.routing.allocation.disk.threshold_enabled | boolean | No | true | ディスク閾値有効/無効 |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| NodesStatsResponse | API応答 | FS統計、ファイルキャッシュ統計、リソース使用統計 |
| IndicesStatsResponse | API応答 | シャードサイズ、ストア統計、データパス |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| leastAvailableSpaceUsages | Map<String, DiskUsage> | ノード別最小空きディスク使用量 |
| mostAvailableSpaceUsages | Map<String, DiskUsage> | ノード別最大空きディスク使用量 |
| nodeFileCacheStats | Map<String, AggregateFileCacheStats> | ウォームノードのファイルキャッシュ統計 |
| nodeResourceUsageStats | Map<String, NodeResourceUsageStats> | ノード別リソース使用統計 |
| indicesStatsSummary | IndicesStatsSummary | シャードサイズ、データパスマッピング、予約容量 |

### 出力ファイル仕様

ファイル出力はなし。

## 処理フロー

### 処理シーケンス

```
1. RefreshAndRescheduleRunnable.doRun()実行
   └─ refresh()を呼び出し
2. refresh()
   ├─ updateNodeStats(): NodesStatsリクエスト送信
   │   ├─ FS統計からディスク使用量を計算
   │   ├─ ファイルキャッシュ統計を収集（ウォームノードのみ）
   │   └─ リソース使用統計を収集
   ├─ updateIndicesStats(): IndicesStatsリクエスト送信
   │   ├─ シャードサイズを収集
   │   ├─ データパスマッピングを構築
   │   └─ 予約容量を計算
   ├─ 各リクエストのタイムアウト待ち（fetchTimeout）
   ├─ ClusterInfoオブジェクトを構築
   └─ 全リスナーに通知
3. onAfter()
   └─ 次回実行をupdateFrequency後にスケジュール
```

### フローチャート

```mermaid
flowchart TD
    A[RefreshAndRescheduleRunnable開始] --> B{enabled?}
    B -->|No| C[スキップ]
    B -->|Yes| D[refresh 開始]
    D --> E[NodesStatsリクエスト送信]
    D --> F[IndicesStatsリクエスト送信]
    E --> G[ディスク使用量計算]
    E --> H[リソース使用統計収集]
    F --> I[シャードサイズ収集]
    G --> J[タイムアウト待ち]
    H --> J
    I --> J
    J --> K[ClusterInfo構築]
    K --> L[リスナー通知]
    L --> M[次回実行スケジュール]
    C --> M
```

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

### 操作別データベース影響一覧

本バッチはデータベース操作を行わない。クラスタ内部APIを使用した統計情報の収集のみを行う。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| なし | ReceiveTimeoutTransportException | NodesStats/IndicesStatsリクエストのタイムアウト | エラーログ出力。該当統計をクリア |
| なし | ClusterBlockException | クラスタブロック中のリクエスト | トレースログ出力。該当統計をクリア |
| なし | Exception | その他のリクエスト失敗 | 警告ログ出力。該当統計をクリア |
| なし | OpenSearchRejectedExecutionException | スレッドプール拒否 | シャットダウン中はDEBUG、それ以外はWARNログ |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 明示的なリトライなし（定期実行により自動再試行） |
| リトライ間隔 | 次回定期実行間隔（デフォルト30秒） |
| リトライ対象エラー | すべてのException |

### 障害時対応

統計取得失敗時は、該当する統計情報がクリア（空のMap）される。これにより、古い情報に基づく誤った判断を防ぐ。次回実行で最新の統計が再取得される。タイムアウトが頻発する場合は`cluster.info.update.timeout`の増加を検討する。

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | なし（各統計情報はvolatileフィールドで独立して更新） |
| コミットタイミング | 各リクエストのコールバック完了時 |
| ロールバック条件 | エラー時は統計情報をクリア |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | データノード数 + 全シャード数 |
| 目標処理時間 | fetchTimeout以内（デフォルト15秒） |
| メモリ使用量上限 | ノード数 x DiskUsageオブジェクト + シャード数 x Long |

## 排他制御

refreshAndRescheduleRunnableはAtomicReferenceで管理され、クラスタマネージャ変更時に原子的に更新される。RefreshAndRescheduleRunnableのdoRun()内でthis==refreshAndRescheduleRunnable.get()チェックを行い、古いインスタンスの実行を防止する。各統計情報はvolatileフィールドで管理され、refresh()内でのコールバックから安全に更新される。

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| トレースログ | クラスタマネージャ選出時 | "elected as cluster-manager, scheduling cluster info update tasks" |
| トレースログ | リフレッシュ開始時 | "refreshing cluster info in background [{reason}]" |
| 警告ログ | ノード統計タイムアウト時 | "Failed to update node information for ClusterInfoUpdateJob within {timeout} timeout" |
| 警告ログ | シャード統計タイムアウト時 | "Failed to update shard information for ClusterInfoUpdateJob within {timeout} timeout" |
| エラーログ | NodesStatsタイムアウト時 | "NodeStatsAction timed out for ClusterInfoUpdateJob" |
| 警告ログ | NodesStats失敗時 | "Failed to execute NodeStatsAction for ClusterInfoUpdateJob" |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| ディスク使用率 | cluster.routing.allocation.disk.watermark設定 | DiskThresholdDecider |
| 統計更新間隔 | cluster.info.update.interval | OpenSearchログ |
| リクエストタイムアウト | cluster.info.update.timeout（デフォルト15秒） | OpenSearchログ |

## 備考

- 実装ファイル: `server/src/main/java/org/opensearch/cluster/InternalClusterInfoService.java`
- ClusterInfoServiceインターフェースとClusterStateListenerを実装している
- MANAGEMENTスレッドプール（ThreadPool.Names.MANAGEMENT）で実行される
- クラスタマネージャノードでのみ動作する。クラスタマネージャが変更されると、旧マネージャの定期タスクは自動的に停止する
- データノード追加時にも即座にリフレッシュが実行される
- 削除されたデータノードの統計情報はclusterChanged()で即座にクリーンアップされる
- NodesStatsリクエストはFS、FILE_CACHE_STATS、RESOURCE_USAGE_STATSメトリクスを要求する
- cluster.info.update.intervalとcluster.info.update.timeoutは動的に変更可能
