# 通知設計書 39-時刻同期モニター（ClockMonitor）

## 概要

本ドキュメントは、Jenkins のノードモニター機能のうち、エージェントノードとコントローラー間の時刻差異を監視する `ClockMonitor` の設計について記述する。

### 本通知の処理概要

時刻同期モニター（ClockMonitor）は、Jenkins のエージェントノードの健全性を監視する `NodeMonitor` の一種であり、コントローラーとエージェント間のシステム時刻の差異を定期的にチェックし、ノード一覧画面に表示する。

**業務上の目的・背景**：分散ビルド環境では、コントローラーとエージェント間の時刻が同期していることが重要である。時刻の差異が大きいと、ファイルのタイムスタンプの不整合、ビルドログの時系列の混乱、証明書の検証エラー、スケジュールされたタスクの誤動作などの問題が発生する可能性がある。ClockMonitor は、時刻の差異を可視化し、管理者が時刻同期の問題を早期に発見できるようにする。

**通知の送信タイミング**：NodeMonitor は定期的（デフォルト60分間隔）に全てのエージェントノードを監視する。各監視サイクルでコントローラーとエージェント間の時刻差異が計測され、ノード一覧画面に表示される。

**通知の受信者**：全てのユーザーがノード一覧画面で時刻差異を確認できる。ただし、このモニターはノードをオフラインにする機能を持たない（`canTakeOffline()` が false を返す）。

**通知内容の概要**：コントローラーとエージェント間の時刻差異（ミリ秒単位）が表示される。大きな差異がある場合は警告が表示される可能性がある。

**期待されるアクション**：管理者は、時刻差異が大きいノードを特定し、NTP（Network Time Protocol）の設定確認、時刻同期サービスの再起動などの対処を行う。

## 通知種別

- アプリ内通知（ノード一覧画面）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（定期ポーリング） |
| 優先度 | 中（参考情報、オフライン化なし） |
| リトライ | 有（次の監視サイクルで再試行） |

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

全てのユーザーがノード一覧画面で時刻差異を確認できる。

## 通知テンプレート

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

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

### 本文テンプレート

`ClockDifference` クラスの `toString()` メソッドによって生成される文字列が表示される。

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| diff | 時刻差異（ミリ秒） | `ClockDifference.diff` | Yes |

## 送信トリガー・条件

### トリガー一覧

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

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| ノードがnull | ノードが存在しない場合はnullを返す |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[定期監視タイマー発火] --> B[monitor実行]
    B --> C[全ノードに対してcreateCallable実行]
    C --> D{ノードがnull?}
    D -->|Yes| E[nullを返す]
    D -->|No| F[n.getClockDifferenceCallable取得]
    F --> G[Callableをノードで実行]
    G --> H[ClockDifference取得]
    H --> I[監視データ更新]
    E --> I
    I --> J[終了]
```

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

### 参照テーブル一覧

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

### 参照項目詳細

該当なし（時刻計測による取得）

### 更新テーブル一覧

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

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ノード未接続 | ノードとの通信が確立できない場合 | nullを返し、次のサイクルで再試行 |
| Callable取得失敗 | getClockDifferenceCallable()がnullを返す場合 | nullを返す |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 無制限（次の監視サイクルで自動再試行） |
| リトライ間隔 | 監視間隔（デフォルト60分） |

## 配信設定

### レート制限

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

### 配信時間帯

制限なし（常時監視）

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

- 時刻差異情報はシステムの状態を示す情報であり、特に機密性は低い
- 大きな時刻差異はセキュリティ上の問題（証明書検証エラーなど）を示唆する可能性がある

## 備考

- `ClockMonitor` は `@Symbol("clock")` アノテーションを持ち、JCasC で `clock` として設定可能
- このモニターは `canTakeOffline()` が false を返すため、ノードを自動的にオフラインにしない
- Jenkins 1.123 から存在する
- 時刻差異の計測は `Node.getClockDifferenceCallable()` メソッドによって行われ、ノードの種類に応じた実装が使用される

---

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

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

### 推奨読解順序

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ClockDifference.java | `core/src/main/java/hudson/util/ClockDifference.java` | ClockDifferenceクラス。diff フィールドで時刻差異（ミリ秒）を保持 |

**読解のコツ**: `ClockDifference` は `hudson.util` パッケージに定義されており、時刻差異を表現するシンプルなデータクラスである。

#### Step 2: モニター本体を理解する

ClockMonitor の実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ClockMonitor.java | `core/src/main/java/hudson/node_monitors/ClockMonitor.java` 47-91行 | ClockMonitorの実装 |
| 2-2 | ClockMonitor.java | `core/src/main/java/hudson/node_monitors/ClockMonitor.java` 53-55行 | getDifferenceFor()メソッド |

**主要処理フロー**:
1. **53-55行**: `getDifferenceFor(Computer c)` で特定のコンピュータの時刻差異を取得
2. **66-91行**: DescriptorImpl で監視ロジックを実装

#### Step 3: 時刻差異計測の仕組みを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ClockMonitor.java | `core/src/main/java/hudson/node_monitors/ClockMonitor.java` 79-83行 | createCallable()メソッド |

**主要処理フロー**:
- **80行**: `c.getNode()` でノードを取得
- **82行**: `n.getClockDifferenceCallable()` で時刻差異計測用の Callable を取得

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

```
Timer（定期実行）
    │
    └─ AbstractNodeMonitorDescriptor.triggerUpdate()
           │
           └─ monitor()
                  │
                  └─ createCallable(c)
                         │
                         ├─ c.getNode()
                         │
                         └─ n.getClockDifferenceCallable()
                                │
                                └─ Callable<ClockDifference, IOException>
                                       │
                                       └─ call()
                                              │
                                              ▼
                                        ClockDifference
```

### データフロー図

```
[コントローラー]          [処理]                    [エージェント]

System.currentTimeMillis() ───▶ [記録]
                                   │
                          ───────────────────▶ System.currentTimeMillis()
                                                      │
                                   ◀───────────────────
                                   │
                             [差分計算]
                                   │
                                   ▼
                           ClockDifference
                                   │
                                   └─ diff (ミリ秒)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ClockMonitor.java | `core/src/main/java/hudson/node_monitors/ClockMonitor.java` | ソース | ClockMonitorの実装 |
| ClockDifference.java | `core/src/main/java/hudson/util/ClockDifference.java` | ソース | 時刻差異を表すデータクラス |
| Node.java | `core/src/main/java/hudson/model/Node.java` | ソース | getClockDifferenceCallable()の定義 |
| NodeMonitor.java | `core/src/main/java/hudson/node_monitors/NodeMonitor.java` | ソース | 基底クラス |
| AbstractAsyncNodeMonitorDescriptor.java | `core/src/main/java/hudson/node_monitors/AbstractAsyncNodeMonitorDescriptor.java` | ソース | 非同期監視の基底Descriptor |
| Messages.properties | `core/src/main/resources/hudson/node_monitors/Messages.properties` | リソース | メッセージ定義 |
