# 通知設計書 38-応答時間モニター（ResponseTimeMonitor）

## 概要

本ドキュメントは、Jenkins のノードモニター機能のうち、エージェントノードへの通信の往復応答時間（ラウンドトリップタイム）を監視し、タイムアウトが連続した場合にノードを切断する `ResponseTimeMonitor` の設計について記述する。

### 本通知の処理概要

応答時間モニター（ResponseTimeMonitor）は、Jenkins のエージェントノードの健全性を監視する `NodeMonitor` の一種であり、コントローラーとエージェント間の通信応答時間を定期的に計測する。

**業務上の目的・背景**：分散ビルド環境では、コントローラーとエージェント間のネットワーク接続の健全性が重要である。ネットワーク障害、エージェントのハングアップ、過負荷などにより、通信が遅延またはタイムアウトすることがある。ResponseTimeMonitor は、応答時間を継続的に監視し、連続してタイムアウトが発生した場合にエージェントとの接続を切断することで、無応答のエージェントがビルドリソースを占有し続けることを防ぐ。

**通知の送信タイミング**：NodeMonitor は定期的（デフォルト60分間隔）に全てのエージェントノードを監視する。各監視サイクルでコントローラーとエージェント間の往復応答時間が計測され、過去5回分の履歴が保持される。5回連続でタイムアウト（5秒以上）が発生した場合、エージェントとの接続が切断される。

**通知の受信者**：管理者およびノード一覧ページを閲覧するユーザーに対して、ノード一覧画面の応答時間カラムに情報が表示される。接続が切断された場合、警告メッセージがログに出力される。

**通知内容の概要**：平均応答時間（ミリ秒）が表示される。タイムアウトが発生している場合は、連続タイムアウト回数とともに表示される。

**期待されるアクション**：管理者は、応答時間が遅いノードやタイムアウトが発生しているノードを特定し、ネットワーク接続の確認、エージェントプロセスの再起動、負荷の軽減などの対処を行う。

## 通知種別

- アプリ内通知（ノード一覧画面）
- ログ出力（接続切断時）
- エージェント接続の自動切断

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（定期ポーリング） |
| 優先度 | 高（ノードの可用性に影響） |
| リトライ | 有（次の監視サイクルで再試行） |

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

全てのユーザーがノード一覧画面で応答時間を確認できる。接続切断はログに記録される。

## 通知テンプレート

### ノード一覧画面の場合

| 項目 | 内容 |
|-----|------|
| 形式 | テキスト（カラム表示） |

### 本文テンプレート

正常時：
```
{average}ms
```

タイムアウト発生時：
```
Time out for {failureCount} time(s)
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| average | 平均応答時間（ミリ秒） | 過去5回の計測値から算出 | Yes |
| failureCount | 連続タイムアウト回数 | past5配列から算出 | No |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| スケジュール | 定期監視（デフォルト60分間隔） | 常に実行 | AbstractNodeMonitorDescriptor による定期実行 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| isIgnored() == true | モニターが無視設定の場合、接続切断しない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[定期監視タイマー発火] --> B[monitor実行]
    B --> C[全ノードに対してcreateCallable実行]
    C --> D[Step1をノードに送信]
    D --> E[Step2に変換されてノードで実行]
    E --> F[Step3を返す]
    F --> G[Step3がreadResolveでData生成]
    G --> H[応答時間を計算]
    H --> I{5回連続タイムアウト?}
    I -->|Yes| J{isIgnored?}
    J -->|No| K[c.disconnectノードを切断]
    J -->|Yes| L[切断スキップ]
    I -->|No| M[データ更新]
    K --> N[警告ログ出力]
    L --> M
    N --> M
    M --> O[終了]
```

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

### 参照テーブル一覧

| ストレージ | 用途 | 備考 |
|-----------|------|------|
| nodeMonitors.xml | NodeMonitor設定の永続化 | |

### 参照項目詳細

該当なし（ネットワーク計測による取得）

### 更新テーブル一覧

該当なし（モニターデータはメモリ上のみで管理）

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| タイムアウト | 応答が5秒以内に返らない場合 | -1を記録し、連続5回でノードを切断 |
| 通信失敗 | ノードとの通信が確立できない場合 | nullを返し、次のサイクルで再試行 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| タイムアウト閾値 | 5000ミリ秒（5秒） |
| 連続タイムアウト閾値 | 5回 |
| リトライ間隔 | 監視間隔（デフォルト60分） |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 監視間隔 | デフォルト60分 |

### 配信時間帯

制限なし（常時監視）

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

- 応答時間情報はノードのネットワーク状態を示す情報であり、特に機密性は低い
- 接続切断はノードの可用性に影響するため、適切な閾値設定が重要

## 備考

- `ResponseTimeMonitor` は `@Symbol("responseTime")` アノテーションを持ち、JCasC で `responseTime` として設定可能
- 他のモニター（ディスク容量など）とは異なり、このモニターは `markOffline()` ではなく `c.disconnect()` を使用してエージェントを切断する
- 切断されたエージェントは、チャネルが閉じられるため、再接続が必要となる
- 応答時間の計測は、シリアライズ/デシリアライズの往復時間を計測する独自の方式を使用

---

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

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

### 推奨読解順序

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

まず、応答時間データを表現するデータ構造を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ResponseTimeMonitor.java | `core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java` 163-227行 | Dataクラス。past5配列で過去5回分の計測値を保持 |

**読解のコツ**: `Data` クラスは `MonitorOfflineCause` を継承しており、ノードの切断理由として使用される。`past5` 配列に -1 が格納されている場合はタイムアウトを示す。

#### Step 2: 計測メカニズムを理解する

応答時間の計測方法を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ResponseTimeMonitor.java | `core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java` 106-124行 | Step1クラス。コントローラー側で開始 |
| 2-2 | ResponseTimeMonitor.java | `core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java` 126-141行 | Step2クラス。エージェント側で実行、開始時刻を記録 |
| 2-3 | ResponseTimeMonitor.java | `core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java` 143-158行 | Step3クラス。コントローラー側で受信、往復時間を計算 |

**主要処理フロー**:
1. **106行**: Step1がコントローラーからエージェントに送信される
2. **119行**: `writeReplace()` でStep2に変換される（シリアライズ時）
3. **128行**: Step2がエージェント側で実行され、開始時刻を記録
4. **135行**: Step3を返す
5. **152-154行**: Step3の `readResolve()` でデシリアライズ時に終了時刻を記録し、往復時間を計算

#### Step 3: 監視とアクションを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ResponseTimeMonitor.java | `core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java` 70-97行 | monitor()メソッドのオーバーライド |

**主要処理フロー**:
- **82-84行**: 監視失敗時は -1 で Data を生成
- **86-93行**: 5回連続タイムアウト（`hasTooManyTimeouts()`）の場合、`c.disconnect(d)` でノードを切断

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

```
Timer（定期実行）
    │
    └─ AbstractNodeMonitorDescriptor.triggerUpdate()
           │
           └─ monitor()
                  │
                  ├─ monitorDetailed()
                  │      └─ createCallable(c)
                  │             └─ Step1(cur)
                  │                    │
                  │                    └─ writeReplace() → Step2
                  │                           │
                  │                           └─ call() → Step3
                  │                                  │
                  │                                  └─ readResolve() → Data
                  │
                  └─ [後処理]
                         │
                         ├─ [失敗] Data(cur, -1)
                         │
                         └─ [hasTooManyTimeouts]
                                └─ c.disconnect(d)
```

### データフロー図

```
[コントローラー]          [ネットワーク]          [エージェント]

Step1(cur) ───────────────────────────────▶
       │                                        │
       │ writeReplace()                         │
       ▼                                        │
Step2(cur, start) ─────────────────────▶ call()
                                                │
                                                ▼
                                          Step3(cur, start)
                                                │
       ◀───────────────────────────────────────┘
       │
       │ readResolve()
       ▼
Data(cur, end - start)
       │
       ├─ getAverage()
       └─ hasTooManyTimeouts()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ResponseTimeMonitor.java | `core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java` | ソース | ResponseTimeMonitorの実装 |
| NodeMonitor.java | `core/src/main/java/hudson/node_monitors/NodeMonitor.java` | ソース | 基底クラス |
| AbstractAsyncNodeMonitorDescriptor.java | `core/src/main/java/hudson/node_monitors/AbstractAsyncNodeMonitorDescriptor.java` | ソース | 非同期監視の基底Descriptor |
| MonitorOfflineCause.java | `core/src/main/java/hudson/node_monitors/MonitorOfflineCause.java` | ソース | オフライン理由の基底クラス |
| Messages.properties | `core/src/main/resources/hudson/node_monitors/Messages.properties` | リソース | メッセージ定義 |
