# 画面設計書 82-強制マージ

## 概要

本ドキュメントは、OpenSearchの強制マージ（Force Merge）REST APIエンドポイントの画面設計書である。インデックス内のLuceneセグメントを強制的にマージし、セグメント数を削減することでクエリパフォーマンスを最適化するAPIである。

### 本画面の処理概要

強制マージAPIは、1つ以上のインデックスに対してLuceneセグメントのマージ操作を強制的に実行する。通常、Luceneは自動的にバックグラウンドでセグメントマージを行うが、本APIを使用することで任意のタイミングでマージを制御できる。

**業務上の目的・背景**：インデックスへのドキュメント登録・更新・削除を繰り返すと、Luceneセグメントが断片化しクエリパフォーマンスが低下する。特に、削除済みドキュメントのマーカーが蓄積する場合や、読み取り専用となったインデックス（ログデータ等の時系列インデックス）に対して、セグメント数を最適化（通常は1セグメントに統合）することで検索性能を大幅に改善できる。本APIはこのような運用上のニーズに応えるものである。

**画面へのアクセス方法**：HTTPクライアントから `POST /{index}/_forcemerge` または `POST /_forcemerge` エンドポイントへリクエストを送信する。

**主要な操作・処理内容**：
1. 対象インデックスを指定して強制マージリクエストを送信する
2. `max_num_segments`パラメータで目標セグメント数を指定する（例：1セグメントへの統合）
3. `only_expunge_deletes`パラメータで削除済みドキュメントの完全除去のみを行うことも可能
4. `wait_for_completion=false`で非同期実行し、タスクとして管理することも可能
5. `primary_only=true`でプライマリシャードのみに対してマージを実行することも可能

**画面遷移**：インデックス作成（No.38）やドキュメント操作の後、パフォーマンス最適化のために使用される。非同期実行時はタスク一覧（No.112）・タスク取得（No.113）で進捗を確認できる。マージ後はセグメント情報（No.85）で結果を確認することが多い。

**権限による表示制御**：インデックスの管理権限が必要である。権限がない場合は403エラーが返される。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 117 | フォースマージ | 主機能 | 1つ以上のインデックスで強制マージを実行する処理 |

## 画面種別

操作実行（APIエンドポイント）

## URL/ルーティング

| メソッド | パス | 説明 |
|---------|------|------|
| POST | `/_forcemerge` | 全インデックスに対して強制マージを実行 |
| POST | `/{index}/_forcemerge` | 指定インデックスに対して強制マージを実行 |

## 入出力項目

### パスパラメータ

| パラメータ名 | 型 | 必須 | 説明 |
|-------------|------|------|------|
| index | list | 任意 | カンマ区切りのインデックス名。`_all`または空文字で全インデックス対象 |

### クエリパラメータ

| パラメータ名 | 型 | デフォルト | 説明 |
|-------------|------|-----------|------|
| max_num_segments | number | dynamic | マージ後の目標セグメント数。通常は1を指定して完全統合する |
| only_expunge_deletes | boolean | false | 削除済みドキュメントの完全除去のみを行うか |
| flush | boolean | true | 強制マージ後にフラッシュを実行するか |
| wait_for_completion | boolean | true | falseの場合は即座にタスクIDを返しバックグラウンドで実行 |
| primary_only | boolean | false | プライマリシャードのみに対してマージを実行するか |
| ignore_unavailable | boolean | false | 利用不可のインデックスを無視するか |
| allow_no_indices | boolean | true | ワイルドカードが具体的なインデックスに解決されない場合に無視するか |
| expand_wildcards | enum | open | ワイルドカード展開の対象（open/closed/hidden/none/all） |

## 表示項目

### レスポンス（同期実行時）

| フィールド | 型 | 説明 |
|-----------|------|------|
| _shards.total | integer | マージ対象の総シャード数 |
| _shards.successful | integer | マージ成功のシャード数 |
| _shards.failed | integer | マージ失敗のシャード数 |

### レスポンス（非同期実行時：wait_for_completion=false）

| フィールド | 型 | 説明 |
|-----------|------|------|
| task | string | タスクID（`nodeId:taskId`形式） |

## イベント仕様

### 1-強制マージ実行（同期）

クライアントがリクエストを送信すると、`RestForceMergeAction.prepareRequest()`（73行目）で`ForceMergeRequest`が構築される。`wait_for_completion=true`（デフォルト）の場合、87行目の分岐により`client.admin().indices().forceMerge()`を通じて同期的に実行される。

### 2-強制マージ実行（非同期）

`wait_for_completion=false`の場合、88-95行目の分岐により`mergeRequest.setShouldStoreResult(true)`が設定され、`client.executeLocally()`でタスクとして非同期実行される。即座にタスクIDが返却される。

### 3-非推奨パラメータ組み合わせ警告

`only_expunge_deletes`と`max_num_segments`を同時に指定した場合、80-85行目で非推奨警告がログに出力される。将来のバージョンではこの組み合わせは拒否される予定。

## データベース更新仕様

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 強制マージ実行 | Lucene Segments | MERGE | 複数セグメントを統合し新しいセグメントを生成 |
| 強制マージ後フラッシュ | Translog | CLEAR | マージ完了後のフラッシュでトランザクションログをクリア |

### テーブル別更新項目詳細

#### Lucene Segments

| 操作 | 項目 | 更新値・取得条件 | 備考 |
|-----|------|-----------------|------|
| MERGE | セグメントファイル | 複数の小セグメントを統合して新セグメントを生成 | 削除マーカー付きドキュメントは物理削除される |

## メッセージ仕様

| メッセージID | 種別 | メッセージ | 発生条件 |
|-------------|------|-----------|---------|
| - | 成功 | `{"_shards":{"total":N,"successful":N,"failed":0}}` | 強制マージ正常完了（同期） |
| - | 成功 | `{"task":"nodeId:taskId"}` | 強制マージ非同期開始 |
| - | 警告 | only_expunge_deletesとmax_num_segmentsの同時指定は非推奨 | 両パラメータを同時指定した場合 |
| - | エラー | `{"error":{"type":"index_not_found_exception",...}}` | 指定インデックスが存在しない |

## 例外処理

| 例外 | HTTPステータス | 説明 |
|------|--------------|------|
| IndexNotFoundException | 404 | 指定したインデックスが存在しない場合 |
| IllegalArgumentException | 400 | 無効なパラメータが指定された場合 |
| ClusterBlockException | 403 | インデックスがブロックされている場合 |

## 備考

- 強制マージは計算コストが高い操作であり、書き込みが活発なインデックスに対しては推奨されない。
- 読み取り専用のインデックス（ログインデックス等）に対して使用するのが最適。
- `max_num_segments=1`で完全統合する場合、一時的にディスク使用量が増加する点に注意。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | indices.forcemerge.json | `rest-api-spec/src/main/resources/rest-api-spec/api/indices.forcemerge.json` | REST APIの仕様定義。max_num_segments, only_expunge_deletes等の全パラメータを把握 |
| 1-2 | ForceMergeRequest.java | `server/src/main/java/org/opensearch/action/admin/indices/forcemerge/ForceMergeRequest.java` | リクエストデータ構造。Defaultsクラス内のMAX_NUM_SEGMENTSデフォルト値を確認 |

**読解のコツ**: `ForceMergeRequest`は`BroadcastRequest`を継承しており、各シャードにブロードキャストされるパターンである。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | RestForceMergeAction.java | `server/src/main/java/org/opensearch/rest/action/admin/indices/RestForceMergeAction.java` | RESTハンドラ。同期・非同期の分岐ロジック（86-95行目）が重要 |

**主要処理フロー**:
1. **64行目**: `routes()`で`POST /_forcemerge`と`POST /{index}/_forcemerge`を登録
2. **74-79行目**: リクエストパラメータを解析し`ForceMergeRequest`を構築
3. **80-85行目**: `only_expunge_deletes`と`max_num_segments`の同時指定時に非推奨警告
4. **86-95行目**: `wait_for_completion`に基づく同期・非同期の分岐

#### Step 3: トランスポート層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | TransportForceMergeAction.java | `server/src/main/java/org/opensearch/action/admin/indices/forcemerge/TransportForceMergeAction.java` | フォースマージのトランスポート層実装 |

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

```
RestForceMergeAction.prepareRequest() [L73-96]
    |
    +-- ForceMergeRequest() [リクエスト構築]
    |       +-- IndicesOptions.fromRequest()
    |
    +-- [同期実行: wait_for_completion=true]
    |       +-- client.admin().indices().forceMerge() [L87]
    |               +-- TransportForceMergeAction.execute()
    |
    +-- [非同期実行: wait_for_completion=false]
            +-- mergeRequest.setShouldStoreResult(true) [L90]
            +-- client.executeLocally(ForceMergeAction.INSTANCE, ...) [L93]
                    +-- LoggingTaskListener.instance()
```

### データフロー図

```
[入力]                           [処理]                           [出力]

HTTP POST /{index}/_forcemerge   RestForceMergeAction             ForceMergeResponse
  +-- index (path)          ---> .prepareRequest() [L73]     ---> (同期) {_shards: {...}}
  +-- max_num_segments           ForceMergeRequest [構築]          (非同期) {task: "..."}
  +-- only_expunge_deletes       TransportForceMergeAction
  +-- wait_for_completion        Lucene IndexWriter.forceMerge()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| indices.forcemerge.json | `rest-api-spec/src/main/resources/rest-api-spec/api/indices.forcemerge.json` | API仕様 | REST APIの仕様定義 |
| RestForceMergeAction.java | `server/src/main/java/org/opensearch/rest/action/admin/indices/RestForceMergeAction.java` | ソース | RESTハンドラ（同期・非同期分岐） |
| ForceMergeRequest.java | `server/src/main/java/org/opensearch/action/admin/indices/forcemerge/ForceMergeRequest.java` | ソース | リクエストデータ構造 |
| ForceMergeAction.java | `server/src/main/java/org/opensearch/action/admin/indices/forcemerge/ForceMergeAction.java` | ソース | アクション定義 |
| TransportForceMergeAction.java | `server/src/main/java/org/opensearch/action/admin/indices/forcemerge/TransportForceMergeAction.java` | ソース | トランスポートアクション |
| RestForceMergeActionTests.java | `server/src/test/java/org/opensearch/action/admin/indices/forcemerge/RestForceMergeActionTests.java` | テスト | RESTハンドラのテスト |
