# バッチ設計書 13-SearchService Reaper

## 概要

本ドキュメントは、OpenSearchの期限切れ検索コンテキスト（ReaderContext）を定期的に解放するバッチ処理「SearchService Reaper」の設計仕様を記載する。

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

SearchService Reaperは、SearchService内部で管理されるアクティブなReaderContext（検索コンテキスト）を定期的にスキャンし、keepAlive期間を超過した期限切れコンテキストを解放するバッチ処理である。

**業務上の目的・背景**：OpenSearchではスクロール検索やPoint-in-Time (PIT) 検索で使用されるReaderContextが一定時間保持される。これらのコンテキストはインデックスセグメントファイルの参照を保持するため、期限切れのコンテキストが解放されないとファイルハンドルやメモリの無駄な消費が続く。また、セグメントのマージ後もコンテキストが古いセグメントを参照し続けるため、ディスク容量の回収も遅延する。本バッチは、期限切れコンテキストを定期的に検出・解放することで、これらのリソースリークを防止する。

**バッチの実行タイミング**：`search.keep_alive_interval`設定に基づく定期実行（デフォルト1分間隔）。SearchServiceの初期化時にscheduleWithFixedDelayで開始される。

**主要な処理内容**：
1. activeReaders（ConcurrentHashMap）内の全ReaderContextをイテレーション
2. 各ReaderContextのisExpired()メソッドで期限切れ判定
3. 期限切れのコンテキストに対してfreeReaderContext()を呼び出し、リソースを解放

**前後の処理との関連**：ReaderContextはSearchService.createAndPutReaderContext()やcreatePitReaderContext()で作成される。各検索リクエストの処理中にmarkAsUsed()でkeepaliveタイマーが更新される。本バッチはこれらとは非同期に動作し、更新されなくなったコンテキストを回収する。

**影響範囲**：スクロール検索、PIT検索のReaderContextに影響する。期限切れコンテキストの解放により、対応するインデックスセグメント参照、ファイルハンドル、メモリが解放される。

## バッチ種別

リソース管理 / メモリ・ファイルハンドル解放

## 実行スケジュール

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

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| SearchServiceが起動済みであること | activeReadersマップが利用可能であること |
| ノードがシャットダウン中でないこと | scheduleWithFixedDelayによる自動停止 |

### 実行可否判定

特別な実行可否判定はない。scheduleWithFixedDelayにより固定間隔で常時実行される。SearchService.doClose()時にkeepAliveReaper.cancel()で停止される。

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| search.keep_alive_interval | TimeValue | No | 1m | Reaperの実行間隔 |
| search.default_keep_alive | TimeValue | No | 5m | デフォルトのkeepAlive時間 |
| search.max_keep_alive | TimeValue | No | 24h | 最大keepAlive時間 |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| activeReaders | ConcurrentHashMap<ShardSearchContextId, ReaderContext> | アクティブな検索コンテキストのマップ |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| activeReaders | マップ更新 | 期限切れコンテキストの削除 |
| ヒープメモリ / ファイルハンドル | リソース解放 | ReaderContext関連リソースの解放 |

### 出力ファイル仕様

ファイル出力はなし。

## 処理フロー

### 処理シーケンス

```
1. Reaper.run()実行
   └─ activeReaders.values()をイテレーション
2. 各ReaderContextに対してisExpired()チェック
   └─ lastAccessTime + keepAlive < currentTime であれば期限切れ
3. 期限切れの場合
   ├─ デバッグログ出力: "freeing search context [contextId]"
   └─ freeReaderContext(context.id())を呼び出し
4. freeReaderContext()内の処理
   ├─ activeReadersからコンテキストを削除
   └─ ReaderContextのclose()を呼び出し（リソース解放）
```

### フローチャート

```mermaid
flowchart TD
    A[Reaper.run 開始] --> B[activeReadersをイテレーション]
    B --> C{次のReaderContextあり?}
    C -->|あり| D{isExpired?}
    D -->|Yes| E[デバッグログ出力]
    E --> F[freeReaderContext 呼び出し]
    F --> C
    D -->|No| C
    C -->|なし| G[Reaper.run 終了]
```

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

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

本バッチはデータベース操作を行わない。インメモリのReaderContextの管理のみを行う。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| なし | 例外 | freeReaderContext()内での例外 | freeReaderContext()内部でハンドリング。Reaperは次のコンテキストの処理を継続 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 明示的なリトライなし（次回定期実行で再試行） |
| リトライ間隔 | 次回定期実行間隔（デフォルト1分） |
| リトライ対象エラー | すべての例外 |

### 障害時対応

Reaperは単純なイテレーション処理であり、個別のコンテキスト解放失敗は次回実行で再試行される。大量のスクロールコンテキストが蓄積する場合は、`search.max_open_scroll_context`設定やkeepalive設定の見直しを検討する。

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | なし（各コンテキストの解放は独立） |
| コミットタイミング | 各freeReaderContext()呼び出し時に即座に反映 |
| ロールバック条件 | 該当なし |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | アクティブなReaderContextの数に依存 |
| 目標処理時間 | ミリ秒単位（コンテキスト数に比例） |
| メモリ使用量上限 | 該当なし（メモリ解放処理） |

## 排他制御

ReaperはThreadPool.Names.SAMEスレッドでscheduleWithFixedDelayにより実行されるため、前回実行の完了後に次回がスケジュールされる。activeReadersはConcurrentHashMapであるため、他スレッドからの追加・削除と並行して安全にイテレーションできる。freeReaderContext()は個別のReaderContextに対してatomicに動作する。

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| デバッグログ | 期限切れコンテキスト検出時 | "freeing search context [{contextId}]" |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| アクティブスクロールコンテキスト数 | search.max_open_scroll_contextで設定 | OpenSearchログ |
| 検索コンテキストの保持時間 | search.max_keep_aliveで設定（デフォルト24h） | OpenSearchログ |

## 備考

- Reaperは SearchService内部のインナークラスとして実装されている
- 実装ファイル: `server/src/main/java/org/opensearch/search/SearchService.java`（1841行目付近）
- ThreadPool.Names.SAMEを使用してscheduleWithFixedDelayでスケジュールされる
- SearchService.doClose()時にkeepAliveReaper.cancel()で明示的に停止される
- PIT（Point-in-Time）コンテキストもReaderContextの一種であり、同一のReaperで管理される
