# バッチ設計書 15-JvmGcMonitorService

## 概要

本ドキュメントは、JVMのガベージコレクション（GC）状態を定期的に監視しログ出力するバッチ処理「JvmGcMonitorService」の設計仕様を記載する。

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

JvmGcMonitorServiceは、JVMのGC統計情報を定期的にポーリングし、GCの遅延やオーバーヘッドが閾値を超過した場合に適切なログレベルで警告を出力するバッチ処理である。内部のJvmMonitor抽象クラスがRunnableとして実装され、scheduleWithFixedDelayで定期実行される。

**業務上の目的・背景**：OpenSearchはJVM上で動作するため、GCの挙動がパフォーマンスに直接影響する。特にOld GCの長時間停止はクエリのレイテンシ増大やクラスタの安定性に影響を与える。本バッチは、GCの遅延時間とGCオーバーヘッド（全体時間に占めるGC時間の割合）を監視し、閾値を超えた場合にオペレータに警告することで、メモリ関連の問題を早期に検出可能にする。

**バッチの実行タイミング**：`monitor.jvm.gc.refresh_interval`設定に基づく定期実行（デフォルト1秒間隔）。doStart()時にscheduleWithFixedDelayで開始される。

**主要な処理内容**：
1. JvmStats.jvmStats()で現在のJVM統計情報を取得
2. 前回取得時からの経過時間を計算
3. 各GCコレクタ（Young/Old等）についてスロー GC検出を実行
4. GCオーバーヘッド（GC時間 / 経過時間の割合）を計算・評価
5. 閾値超過時にDEBUG/INFO/WARNレベルでログ出力

**前後の処理との関連**：本バッチは独立して動作する。GC情報はJVM内部の統計情報から取得するため、他のOpenSearchコンポーネントへの依存はない。出力されるログはログ監視ツールで活用される。

**影響範囲**：ログ出力のみであり、システム動作への直接的な影響はない。ただし、GCオーバーヘッドの情報はオペレータのチューニング判断に使用される。

## バッチ種別

モニタリング / JVMヘルスチェック

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 定期実行（デフォルト1秒間隔） |
| 実行時刻 | ノード起動後、設定間隔ごとに繰り返し実行 |
| 実行曜日 | 該当なし（常時） |
| 実行日 | 該当なし（常時） |
| トリガー | ThreadPool.scheduleWithFixedDelay（Names.SAME） |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| monitor.jvm.gc.enabled がtrueであること | デフォルトtrue。falseの場合はdoStart()でスケジュールされない |
| GC閾値設定が有効であること | warn > info > debug の順序が守られている必要がある |

### 実行可否判定

`monitor.jvm.gc.enabled`がfalseの場合、doStart()で早期リターンしスケジューリングが行われない。GCオーバーヘッド閾値設定においてwarn <= infoまたはinfo <= debugの場合はIllegalArgumentExceptionがスローされる。

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| monitor.jvm.gc.enabled | boolean | No | true | GC監視の有効/無効 |
| monitor.jvm.gc.refresh_interval | TimeValue | No | 1s | 監視間隔（最小1秒） |
| monitor.jvm.gc.collector.young.warn | TimeValue | No | 1000ms | Young GCの警告閾値 |
| monitor.jvm.gc.collector.young.info | TimeValue | No | 700ms | Young GCの情報閾値 |
| monitor.jvm.gc.collector.young.debug | TimeValue | No | 400ms | Young GCのデバッグ閾値 |
| monitor.jvm.gc.collector.old.warn | TimeValue | No | 10000ms | Old GCの警告閾値 |
| monitor.jvm.gc.collector.old.info | TimeValue | No | 5000ms | Old GCの情報閾値 |
| monitor.jvm.gc.collector.old.debug | TimeValue | No | 2000ms | Old GCのデバッグ閾値 |
| monitor.jvm.gc.overhead.warn | int | No | 50 | GCオーバーヘッド警告閾値（%） |
| monitor.jvm.gc.overhead.info | int | No | 25 | GCオーバーヘッド情報閾値（%） |
| monitor.jvm.gc.overhead.debug | int | No | 10 | GCオーバーヘッドデバッグ閾値（%） |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| JvmStats.jvmStats() | JVM内部API | JVMのGC統計情報（コレクション回数、コレクション時間、ヒープ使用量等） |
| System.nanoTime() | JVM内部API | 経過時間の計算に使用 |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| OpenSearchログ | ログ出力 | GC遅延警告、GCオーバーヘッド警告 |

### 出力ファイル仕様

ファイル出力はなし（ログファイルへの出力のみ）。

## 処理フロー

### 処理シーケンス

```
1. JvmMonitor.run()実行
   └─ monitorGc()を呼び出し
2. monitorGc()（synchronized）
   ├─ シーケンス番号をインクリメント
   ├─ 現在のJvmStatsを取得
   ├─ 前回からの経過時間をミリ秒で計算
   ├─ monitorSlowGc()を呼び出し
   └─ monitorGcOverhead()を呼び出し
3. monitorSlowGc()
   ├─ 各GCコレクタについてループ
   ├─ コレクション回数の差分を計算
   ├─ 平均コレクション時間 = コレクション時間差分 / コレクション回数差分
   ├─ 閾値と比較してWARN/INFO/DEBUGレベルを決定
   └─ onSlowGc()コールバックでログ出力
4. monitorGcOverhead()
   ├─ 全コレクタのGC時間合計を計算
   ├─ GCオーバーヘッド = (GC時間 * 100) / 経過時間
   ├─ 閾値と比較してWARN/INFO/DEBUGレベルを決定
   └─ onGcOverhead()コールバックでログ出力
5. 前回統計情報を現在の値で更新
```

### フローチャート

```mermaid
flowchart TD
    A[JvmMonitor.run 開始] --> B[monitorGc 呼び出し]
    B --> C[JvmStats取得・経過時間計算]
    C --> D[monitorSlowGc]
    D --> E[各コレクタの平均GC時間を計算]
    E --> F{閾値超過?}
    F -->|Yes| G[onSlowGc コールバック: ログ出力]
    F -->|No| H[次のコレクタ]
    G --> H
    H --> I[monitorGcOverhead]
    I --> J[GCオーバーヘッド計算]
    J --> K{閾値超過?}
    K -->|Yes| L[onGcOverhead コールバック: ログ出力]
    K -->|No| M[前回統計更新]
    L --> M
    M --> N[JvmMonitor.run 終了]
```

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

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

本バッチはデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| なし | IllegalArgumentException | GCオーバーヘッド閾値設定が不正（warn <= info等） | ノード起動時に例外スロー（起動失敗） |
| なし | Exception | monitorGc()実行中の例外 | onMonitorFailure()でデバッグログ出力。次回実行は継続 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 明示的なリトライなし（定期実行により自動再試行） |
| リトライ間隔 | 次回定期実行間隔（デフォルト1秒） |
| リトライ対象エラー | monitorGc()内の例外 |

### 障害時対応

JVM統計情報の取得失敗はデバッグログに記録される。次回実行で自動的に再試行される。GC関連の問題が継続する場合は、JVMのヒープサイズやGC設定の見直しを行う。

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | なし（読み取り専用の監視処理） |
| コミットタイミング | 該当なし |
| ロールバック条件 | 該当なし |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | GCコレクタ数（通常2: Young + Old） |
| 目標処理時間 | マイクロ秒単位（JVM統計の読み取りのみ） |
| メモリ使用量上限 | 微小（JvmStatsオブジェクトのみ） |

## 排他制御

monitorGc()メソッドはsynchronizedで排他制御されており、前回統計情報の更新と現在統計の読み取りが原子的に行われる。scheduleWithFixedDelayを使用しているため、通常は同時実行されない。

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| 警告ログ | スローGC検出時（WARN閾値超過） | "[gc][{name}][{seq}][{count}] duration [{time}], collections [{count}]/[{elapsed}], total [{time}]/[{total}], memory [{prev}]->[{current}]/[{max}], all_pools {pools}" |
| 情報ログ | スローGC検出時（INFO閾値超過） | 同上（INFOレベル） |
| デバッグログ | スローGC検出時（DEBUG閾値超過） | 同上（DEBUGレベル） |
| 警告ログ | GCオーバーヘッド検出時（WARN閾値超過） | "[gc][{seq}] overhead, spent [{time}] collecting in the last [{elapsed}]" |
| 情報ログ | GCオーバーヘッド検出時（INFO閾値超過） | 同上（INFOレベル） |
| デバッグログ | GCオーバーヘッド検出時（DEBUG閾値超過） | 同上（DEBUGレベル） |
| デバッグログ | 監視失敗時 | "failed to monitor" + 例外詳細 |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| Young GC平均時間 | warn: 1000ms, info: 700ms, debug: 400ms | OpenSearchログ |
| Old GC平均時間 | warn: 10000ms, info: 5000ms, debug: 2000ms | OpenSearchログ |
| GCオーバーヘッド | warn: 50%, info: 25%, debug: 10% | OpenSearchログ |

## 備考

- JvmMonitorはJvmGcMonitorService内部の抽象クラスとして実装。doStart()で匿名クラスとしてインスタンス化される
- 実装ファイル: `server/src/main/java/org/opensearch/monitor/jvm/JvmGcMonitorService.java`
- JvmGcMonitorServiceはAbstractLifecycleComponentを継承している
- GCコレクタ名に対応する閾値が未設定の場合は"default"閾値（warn: 10000ms, info: 5000ms, debug: 2000ms）が使用される
- monitor.jvm.gc.collector.{name}.{level}形式で任意のGCコレクタの閾値を設定可能
- GCオーバーヘッド閾値はwarn > info > debugの順序が必須（コンストラクタで検証）
