# バッチ設計書 9-ComputerRetentionWork

## 概要

本ドキュメントは、JenkinsのComputerRetentionWorkバッチ処理の設計について記載する。このバッチはオフラインのエージェントを定期的にチェックし、再接続を試みるエージェント管理機能を提供する。

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

ComputerRetentionWorkは、Jenkinsに登録されている全コンピューター（エージェント）のRetentionStrategy（保持戦略）を定期的に呼び出し、オフラインのエージェントの再接続やオンデマンドエージェントの起動・停止を制御するバッチ処理である。

**業務上の目的・背景**：CI/CD環境では、エージェントがネットワーク障害やシステム再起動などで一時的にオフラインになることがある。手動での再接続は運用負荷が高く、ビルドの遅延を招く。本バッチは、RetentionStrategyを通じて各エージェントの状態を定期的にチェックし、必要に応じて自動的に再接続を試みることで、システムの可用性を維持する。また、オンデマンドエージェント（需要に応じて起動・停止）の制御にも使用される。

**バッチの実行タイミング**：デフォルト60秒間隔で定期実行される。実行間隔はGlobalComputerRetentionCheckIntervalConfigurationで設定可能（1〜60秒）。AperiodicWorkを継承しているため、実行間隔は動的に変更可能。

**主要な処理内容**：
1. 全コンピューターをイテレート
2. 各コンピューターについて、次回チェック時刻が到来しているか確認
3. ノードがholdOffLaunchUntilSave状態でないことを確認
4. RetentionStrategy.check()を呼び出し
5. check()の戻り値（次回チェックまでの待機分数）をnextCheckマップに記録

**前後の処理との関連**：ConnectionActivityMonitorと補完的な関係。ConnectionActivityMonitorは「死んだ接続の切断」、ComputerRetentionWorkは「再接続の試行」を担当。RetentionStrategyプラグイン（例: EC2 Plugin）と連携して動作。

**影響範囲**：RetentionStrategyの実装に依存。標準的なRetentionStrategy（AlwaysOnlineRetentionStrategy）は何もしないが、クラウドプラグインのRetentionStrategyはVMの起動・停止を行う場合がある。

## バッチ種別

エージェント管理 / 再接続・保持戦略

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 設定可能（デフォルト60秒、1〜60秒の範囲） |
| 実行時刻 | 常時（Jenkins稼働中） |
| 実行曜日 | 毎日 |
| 実行日 | 毎日 |
| トリガー | AperiodicWorkによる定期実行 |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| Jenkinsが起動していること | AperiodicWorkの初期化後に自動スケジュール |

### 実行可否判定

- 各コンピューターについて、nextCheckマップに記録された次回チェック時刻を確認
- ノードがholdOffLaunchUntilSave状態（設定保存待ち）の場合はスキップ

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| computerRetentionCheckInterval | int | No | 60 | チェック間隔（秒）。GlobalComputerRetentionCheckIntervalConfigurationで設定。1〜60秒の範囲 |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| Jenkins.get().getComputers() | Computer[] | 全コンピューター |
| Computer.getNode() | Node | コンピューターに対応するノード |
| Computer.getRetentionStrategy() | RetentionStrategy | コンピューターの保持戦略 |
| GlobalComputerRetentionCheckIntervalConfiguration | Configuration | チェック間隔設定 |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| Computer | 状態変更 | RetentionStrategyによる接続・切断など |
| nextCheck（内部マップ） | Map<Computer, Long> | 各コンピューターの次回チェック時刻 |

### 出力ファイル仕様

ファイル出力は行わない（AperiodicWorkはログファイルを自動生成しない）。

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

## 処理フロー

### 処理シーケンス

```
1. doAperiodicRun()メソッドが呼び出される
   └─ AperiodicWorkのタイマーにより定期実行
2. 現在時刻を取得
   └─ startRun = System.currentTimeMillis()
3. 全コンピューターをイテレート
   └─ for (Computer c : Jenkins.get().getComputers())
4. Queue.runWithLock()内で処理
   └─ キューのロックを取得して排他制御
5. ノードの取得とチェック
   └─ Node n = c.getNode()
   └─ n.isHoldOffLaunchUntilSave() の場合はreturn
6. 次回チェック時刻の確認
   └─ !nextCheck.containsKey(c) || startRun > nextCheck.get(c)
7. RetentionStrategy.check()を呼び出し
   └─ waitInMins = c.getRetentionStrategy().check(c)
   └─ 0〜60分の範囲に制限
8. 次回チェック時刻をマップに記録
   └─ nextCheck.put(c, startRun + TimeUnit.MINUTES.toMillis(waitInMins))
```

### フローチャート

```mermaid
flowchart TD
    A[バッチ開始] --> B[現在時刻を取得]
    B --> C[全コンピューターをイテレート]
    C --> D[Queue.runWithLock内で処理]
    D --> E{Node取得可能?}
    E -->|No| C
    E -->|Yes| F{holdOffLaunchUntilSave?}
    F -->|Yes| C
    F -->|No| G{次回チェック時刻到来?}
    G -->|No| C
    G -->|Yes| H[RetentionStrategy.check呼び出し]
    H --> I[待機時間を0-60分に制限]
    I --> J[nextCheckマップを更新]
    J --> C
    C --> K{全コンピューター処理完了?}
    K -->|No| C
    K -->|Yes| L[バッチ終了]
```

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

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

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

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| なし | RuntimeException | RetentionStrategy.check()の失敗 | 例外が伝播、次回実行で再試行 |

### リトライ仕様

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

### 障害時対応

個別のコンピューターのチェックでエラーが発生しても、他のコンピューターのチェックは継続される。エラーはログに記録される。

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

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

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | コンピューター数 |
| 目標処理時間 | RetentionStrategy実装に依存 |
| メモリ使用量上限 | WeakHashMapでComputerへの弱参照を保持 |

## 排他制御

- Queue.runWithLock()を使用してキューのロックを取得
- これにより、ビルドのスケジューリングとRetentionStrategy.check()が競合しない
- nextCheckマップはWeakHashMapを使用し、Computerが参照されなくなると自動的にエントリが削除される

## ログ出力

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

## 監視・アラート

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

## 備考

- シンボル名: `computerRetention`（@Symbolアノテーション）
- AperiodicWorkを継承しており、getRecurrencePeriod()が毎回呼び出されるため、実行間隔を動的に変更可能
- getNewInstance()はthisを返す（シングルトンパターン）
- RetentionStrategy.check()の戻り値は「次回チェックまでの待機分数」
- 待機時間は0〜60分に制限されている（"at the moment I don't trust strategies to wait more than 60 minutes"コメント）
- GlobalComputerRetentionCheckIntervalConfigurationでチェック間隔を設定可能
  - ordinal = 401で高優先度
  - 1〜60秒の範囲に制限
  - 不正な値の場合は60秒にフォールバック
- WeakHashMapを使用することで、削除されたComputerのエントリが自動的にクリーンアップされる
