# バッチ設計書 1-MemoryUsageMonitor

## 概要

本ドキュメントは、JenkinsのMemoryUsageMonitorバッチ処理の設計について記載する。このバッチはJVMのメモリ使用状況を定期的に監視し、統計グラフ用のデータを収集するシステム監視機能を提供する。

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

MemoryUsageMonitorは、JVMのヒープメモリおよび非ヒープメモリの使用量を定期的に収集し、時系列データとして蓄積することで、Jenkinsの管理画面でメモリ使用状況のトレンドグラフを表示可能にするバッチ処理である。

**業務上の目的・背景**：Jenkinsは長時間稼働するCI/CDサーバーであり、メモリリークやメモリ不足によるパフォーマンス劣化を早期に検知する必要がある。本バッチは、JVMのメモリプール（ヒープ・非ヒープ）の使用量と最大容量を継続的に収集することで、管理者がメモリ使用傾向を可視化し、問題発生前に対策を講じることを可能にする。これにより、OutOfMemoryErrorによるビルド失敗やシステムダウンを未然に防止できる。

**バッチの実行タイミング**：10秒間隔で定期実行される。Jenkins起動時に自動的にスケジュールされ、初回実行は0〜10秒のランダムな遅延後に開始される。

**主要な処理内容**：
1. JVMのMemoryPoolMXBeanから全メモリプールの情報を取得
2. ヒープメモリ（HEAP）タイプのプールについてGC後の使用量と最大値を集計
3. 非ヒープメモリ（NON_HEAP）タイプのプールについてGC後の使用量と最大値を集計
4. 収集したデータをMultiStageTimeSeriesオブジェクトに記録（時系列データとして蓄積）

**前後の処理との関連**：独立したバッチ処理であり、他のバッチとの依存関係はない。MemoryUsageMonitorActionを通じてWebUIからアクセスされ、doGraphメソッドでトレンドチャートを生成して表示する。

**影響範囲**：JVMのメモリ統計情報のみを参照する読み取り専用の処理であり、システムの状態を変更しない。収集したデータはメモリ内のMultiStageTimeSeriesオブジェクトに保持され、永続化はされない。

## バッチ種別

システム監視 / メトリクス収集

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 10秒ごと |
| 実行時刻 | 常時（Jenkins稼働中） |
| 実行曜日 | 毎日 |
| 実行日 | 毎日 |
| トリガー | PeriodicWorkによる定期実行 |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| Jenkinsが起動していること | PeriodicWorkの初期化後に自動スケジュール |
| JVMのManagementFactoryが利用可能 | MemoryPoolMXBeanへのアクセスが必要 |

### 実行可否判定

常に実行される。実行をスキップする条件はない。

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| なし | - | - | - | パラメータは存在しない |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| ManagementFactory.getMemoryPoolMXBeans() | JMX | JVMのメモリプール情報を取得 |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| MemoryGroup.used | MultiStageTimeSeries | ヒープ/非ヒープメモリの使用量（KB単位） |
| MemoryGroup.max | MultiStageTimeSeries | ヒープ/非ヒープメモリの最大容量（KB単位） |

### 出力ファイル仕様

ファイル出力は行わない。メモリ内にのみデータを保持する。

| 項目 | 内容 |
|-----|------|
| ファイル名 | なし |
| 出力先 | メモリ（MultiStageTimeSeries） |
| 文字コード | なし |
| 区切り文字 | なし |

## 処理フロー

### 処理シーケンス

```
1. doRun()メソッドが呼び出される
   └─ PeriodicWorkのタイマーにより10秒ごとに実行
2. heap.update()を実行
   └─ ヒープメモリプールの使用量と最大値を更新
3. nonHeap.update()を実行
   └─ 非ヒープメモリプールの使用量と最大値を更新
4. 各MemoryGroup.update()内部処理
   └─ 4.1 対象タイプの全メモリプールをイテレート
   └─ 4.2 各プールのgetCollectionUsage()からGC後のメモリ使用量を取得
   └─ 4.3 使用量と最大値を集計（バイトからKBに変換）
   └─ 4.4 MultiStageTimeSeriesに記録
```

### フローチャート

```mermaid
flowchart TD
    A[バッチ開始] --> B[heap.update 呼び出し]
    B --> C[ヒープメモリプールをイテレート]
    C --> D[各プールのCollectionUsageを取得]
    D --> E{使用量が取得可能?}
    E -->|Yes| F[使用量と最大値を集計]
    E -->|No| G[スキップして次のプールへ]
    F --> H[KB単位に変換]
    G --> C
    H --> I[MultiStageTimeSeriesに記録]
    I --> J[nonHeap.update 呼び出し]
    J --> K[非ヒープメモリプールをイテレート]
    K --> L[同様の処理を実行]
    L --> M[バッチ終了]
```

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

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

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| なし | なし | なし | データベースへのアクセスは行わない |

### テーブル別操作詳細

データベース操作は存在しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| なし | NullPointerException（可能性） | getCollectionUsage()がnullを返す場合 | コード内でnullチェックを行い、スキップする |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし（次回の定期実行で再試行） |
| リトライ間隔 | 10秒（次回実行時） |
| リトライ対象エラー | なし |

### 障害時対応

単一の実行でエラーが発生しても、次回の定期実行は継続される。メモリ統計が一時的に欠損する可能性があるが、時系列データの傾向分析には大きな影響を与えない。

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | なし |
| コミットタイミング | なし |
| ロールバック条件 | なし |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | メモリプール数（通常5〜10程度） |
| 目標処理時間 | 1ミリ秒未満 |
| メモリ使用量上限 | 最小限（既存オブジェクトへの更新のみ） |

## 排他制御

排他制御は実装されていない。PeriodicWorkのdoRun()はシングルスレッドで実行される。

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| 開始ログ | なし | 出力なし |
| 進捗ログ | なし | 出力なし |
| 終了ログ | なし | 出力なし |
| エラーログ | なし | 出力なし |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| なし | なし | なし |

## 備考

- シンボル名: `memoryUsage`（@Symbolアノテーション）
- バイトからKBへの変換は1024で除算
- MultiStageTimeSeriesは複数の時間スケール（秒、分、時間）でデータを保持し、それぞれの時間スケールでの減衰率を適用
- WebUIからは`/hudson.diagnosis.MemoryUsageMonitor/heap/graph`のようなURLでグラフにアクセス可能
- 権限チェック: グラフ表示時にJenkins.SYSTEM_READまたはJenkins.MANAGE権限が必要
