# 機能設計書 5-インデックスロールオーバー

## 概要

本ドキュメントは、OpenSearchにおけるインデックスロールオーバー機能の設計を記述する。条件に基づきエイリアスまたはデータストリームを新しいインデックスへ自動的に切り替える機能である。

### 本機能の処理概要

ロールオーバー機能は、エイリアスの書き込みインデックスまたはデータストリームの書き込みインデックスを、条件（最大ドキュメント数、最大インデックスサイズ、最大経過時間）に基づいて新しいインデックスに切り替える。dry_runモードで条件評価のみを実行することも可能。

**業務上の目的・背景**：時系列データのインデックス管理を自動化し、インデックスサイズの肥大化を防ぐ。適切なサイズのインデックスを維持することで、検索性能とクラスタの安定性を確保する。

**機能の利用シーン**：ログデータの日次/サイズベースのロールオーバー、メトリクスデータの自動インデックス切り替え、ISM（Index State Management）ポリシーからの自動ロールオーバー。

**主要な処理内容**：
1. ロールオーバーターゲット（エイリアスまたはデータストリーム）のバリデーション
2. 新インデックス名の生成（自動採番またはユーザ指定）
3. インデックス統計の取得（ドキュメント数、サイズ）
4. 条件（max_age, max_docs, max_size）の評価
5. 条件充足時：新インデックスの作成とエイリアスの切り替え
6. アクティブシャードの待機

**関連システム・外部連携**：エイリアス機能と密接に連携。データストリーム機能のロールオーバーにも対応。インデックステンプレートが新インデックスに自動適用される。

**権限による制御**：ロールオーバーターゲット（エイリアス/データストリーム）への権限と、新インデックス作成(`indices:admin/create`)権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 48 | インデックスロールオーバー | 主画面 | エイリアスを新しいインデックスに条件付きで切り替える処理 |

## 機能種別

CRUD操作（Create + Update）/ クラスタ状態管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| rolloverTarget | String | Yes | ロールオーバー対象のエイリアスまたはデータストリーム名 | nullでないこと |
| newIndexName | String | No | 新インデックス名（省略時は自動生成） | データストリームの場合は指定不可 |
| dry_run | boolean | No | 条件評価のみ実行するか | デフォルトfalse |
| conditions.max_age | TimeValue | No | インデックスの最大経過時間 | - |
| conditions.max_docs | long | No | インデックスの最大ドキュメント数 | - |
| conditions.max_size | ByteSizeValue | No | インデックスの最大サイズ | - |
| settings | Settings | No | 新インデックスの設定 | データストリームの場合は指定不可 |
| mappings | String | No | 新インデックスのマッピング | データストリームの場合は指定不可 |
| aliases | Set\<Alias\> | No | 新インデックスのエイリアス | データストリームの場合は指定不可 |
| wait_for_active_shards | ActiveShardCount | No | アクティブシャード待機数 | - |

### 入力データソース

REST API（POST /{alias}/_rollover または POST /{alias}/_rollover/{newIndex}）からのHTTPリクエスト。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| old_index | String | ロールオーバー元インデックス名 |
| new_index | String | ロールオーバー先インデックス名 |
| rolled_over | boolean | 実際にロールオーバーが実行されたか |
| dry_run | boolean | dry_runモードだったか |
| acknowledged | boolean | クラスタ状態更新が承認されたか |
| shards_acknowledged | boolean | アクティブシャードが開始されたか |
| conditions | Map\<String, Boolean\> | 各条件の評価結果 |

### 出力先

REST APIレスポンス（JSON形式）

## 処理フロー

### 処理シーケンス

```
1. ロールオーバーターゲットのバリデーション
   └─ エイリアスまたはデータストリームであること、書き込みインデックスが存在すること
2. 新インデックス名の解決
   └─ 自動生成: ソースインデックス名の末尾数値をインクリメント（6桁ゼロ埋め）
3. インデックス統計の取得
   └─ IndicesStatsRequestでドキュメント数を取得
4. 条件の評価
   └─ max_age, max_docs, max_sizeの各条件を評価
5. dry_runの場合はここで条件評価結果を返却
6. 条件未設定 または 条件充足の場合：
   └─ ClusterStateUpdateTaskとしてロールオーバーを実行
7. 新インデックスの作成
   └─ MetadataCreateIndexService.applyCreateIndexRequest()
8. エイリアスの切り替え（エイリアスの場合）
   └─ MetadataIndexAliasesService.applyAliasActions()
9. データストリームのロールオーバー（データストリームの場合）
   └─ DataStream.rollover()
10. RolloverInfoの記録
11. アクティブシャードの待機
```

### フローチャート

```mermaid
flowchart TD
    A[REST API リクエスト受信] --> B[ターゲットバリデーション]
    B -->|無効| ERR1[IllegalArgumentException]
    B -->|OK| C[新インデックス名解決]
    C --> D[インデックス統計取得]
    D --> E[条件評価]
    E --> F{dry_run?}
    F -->|Yes| G1[条件評価結果のみ返却]
    F -->|No| G2{条件充足 or 条件未設定?}
    G2 -->|No| G3[条件不充足レスポンス返却]
    G2 -->|Yes| H[ClusterStateUpdateTask実行]
    H --> I[新インデックス作成]
    I --> J[エイリアス切り替え / DS rollover]
    J --> K[RolloverInfo記録]
    K --> L[アクティブシャード待機]
    L --> M[RolloverResponse返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | ターゲット種別制限 | ロールオーバーターゲットはエイリアスまたはデータストリームであること | 常時 |
| BR-02 | 書き込みインデックス必須 | ターゲットに書き込みインデックスが存在すること | 常時 |
| BR-03 | データストリーム制限 | データストリームの場合、newIndexName, settings, mappings, aliasesは指定不可 | データストリーム対象時 |
| BR-04 | インデックス名自動生成 | ソースインデックス名が`*-数字`パターンの場合、数字をインクリメント（6桁ゼロ埋め） | 新インデックス名未指定時 |
| BR-05 | テンプレート重複チェック | 新インデックス名がテンプレートのエイリアスと重複する場合はエラー | エイリアスロールオーバー時 |
| BR-06 | 同時変更検出 | ロールオーバー実行中にソースインデックスが変更された場合はエラー | ロールオーバー実行時 |

### 計算ロジック

条件評価: numDocs（ドキュメント数）、indexSize（インデックスサイズ）、indexCreated（作成日時）からCondition.Statsを構築し、各条件のevaluate()メソッドで評価する。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 新インデックス作成 | ClusterState.metadata | INSERT | 新インデックスメタデータの追加 |
| エイリアス切り替え | ClusterState.metadata.aliases | UPDATE | エイリアスの付け替え |
| RolloverInfo記録 | ClusterState.metadata.IndexMetadata | UPDATE | ソースインデックスにRolloverInfo追加 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 400 | IllegalArgumentException | ターゲットが存在しない | ターゲット名を確認 |
| 400 | IllegalArgumentException | 書き込みインデックスが未設定 | write indexを設定 |
| 400 | ResourceAlreadyExistsException | 新インデックス名が既存 | 別名を指定 |
| 400 | OpenSearchException | ロールオーバー中の同時変更検出 | 再実行 |

### リトライ仕様

ClusterStateUpdateTaskのリトライ機構が適用される。rolloverIndexTaskKeyによるスロットリングが有効。

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

新インデックスの作成とエイリアスの切り替えは単一のClusterStateUpdateTask内でアトミックに実行される。

## パフォーマンス要件

ロールオーバーはインデックス統計取得（IndicesStatsRequest）とクラスタ状態更新を含むため、シャード数に応じた処理時間が必要。ClusterManagerTaskThrottlerによるスロットリングが適用される。

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

- ロールオーバーターゲットへのアクセス権限が必要
- 新インデックス作成権限（`indices:admin/create`）が必要

## 備考

- RolloverActionのアクション名は`indices:admin/rollover`
- インデックス名の自動生成パターン: `^.*-\d+$`にマッチするインデックスの末尾数値をインクリメント、6桁ゼロ埋め
- データストリームの場合はDataStream.getDefaultBackingIndexName()で新インデックス名を生成

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | RolloverRequest.java | `server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverRequest.java` | rolloverTarget, newIndexName, dryRun, conditions, createIndexRequest |
| 1-2 | Condition.java | `server/src/main/java/org/opensearch/action/admin/indices/rollover/Condition.java` | 条件の抽象クラス。Stats内部クラス（numDocs, indexCreated, indexSize） |
| 1-3 | RolloverResponse.java | `server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverResponse.java` | レスポンス構造 |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | TransportRolloverAction.java | `server/src/main/java/org/opensearch/action/admin/indices/rollover/TransportRolloverAction.java` | メイン処理ロジック |

**主要処理フロー**:
- **151-266行目**: clusterManagerOperation(Task) -- メイン処理
- **158-166行目**: rolloverService.rolloverClusterState() (onlyValidate=true) -- プレチェック
- **170-173行目**: IndicesStatsRequest作成
- **178-182行目**: evaluateConditions() -- 条件評価
- **196-252行目**: ClusterStateUpdateTask -- 実際のロールオーバー実行

#### Step 3: ロールオーバーサービスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | MetadataRolloverService.java | `server/src/main/java/org/opensearch/action/admin/indices/rollover/MetadataRolloverService.java` | ロールオーバーの実体処理 |

**主要処理フロー**:
- **114-151行目**: rolloverClusterState() -- エイリアス/データストリーム分岐
- **153-203行目**: rolloverAlias() -- エイリアスロールオーバー
- **205-245行目**: rolloverDataStream() -- データストリームロールオーバー
- **247-264行目**: generateRolloverIndexName() -- インデックス名自動生成

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

```
REST API (POST /{alias}/_rollover)
    |
    +-- TransportRolloverAction
          |
          +-- rolloverService.rolloverClusterState() (validate only)
          |
          +-- IndicesStatsRequest (ドキュメント数取得)
          |
          +-- evaluateConditions()
          |
          +-- (dry_run) -> 即座にレスポンス返却
          |
          +-- ClusterStateUpdateTask
                |
                +-- rolloverService.rolloverClusterState() (実行)
                |     |
                |     +-- (Alias) rolloverAlias()
                |     |     +-- createIndexService.applyCreateIndexRequest()
                |     |     +-- indexAliasesService.applyAliasActions()
                |     |
                |     +-- (DataStream) rolloverDataStream()
                |           +-- createIndexService.applyCreateIndexRequest()
                |           +-- DataStream.rollover()
                |
                +-- activeShardsObserver.waitForActiveShards()
```

### データフロー図

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

RolloverRequest --------> TransportRolloverAction ---------> RolloverResponse
 (rolloverTarget,          |                                  (old_index,
  newIndexName,             +-> rolloverClusterState(validate)  new_index,
  conditions,               +-> IndicesStatsRequest             rolled_over,
  dryRun,                   +-> evaluateConditions()            conditions,
  createIndexRequest)       +-> ClusterStateUpdateTask          acknowledged)
                                 +-> rolloverClusterState(exec)
                                 +-> waitForActiveShards()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| RolloverAction.java | `server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverAction.java` | ソース | アクション型定義 |
| RolloverRequest.java | `server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverRequest.java` | ソース | リクエスト構造 |
| RolloverResponse.java | `server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverResponse.java` | ソース | レスポンス構造 |
| RolloverInfo.java | `server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverInfo.java` | ソース | ロールオーバー情報 |
| TransportRolloverAction.java | `server/src/main/java/org/opensearch/action/admin/indices/rollover/TransportRolloverAction.java` | ソース | トランスポートアクション |
| MetadataRolloverService.java | `server/src/main/java/org/opensearch/action/admin/indices/rollover/MetadataRolloverService.java` | ソース | ロールオーバーサービス |
| Condition.java | `server/src/main/java/org/opensearch/action/admin/indices/rollover/Condition.java` | ソース | 条件抽象クラス |
| MaxAgeCondition.java | `server/src/main/java/org/opensearch/action/admin/indices/rollover/MaxAgeCondition.java` | ソース | 最大経過時間条件 |
| MaxDocsCondition.java | `server/src/main/java/org/opensearch/action/admin/indices/rollover/MaxDocsCondition.java` | ソース | 最大ドキュメント数条件 |
| MaxSizeCondition.java | `server/src/main/java/org/opensearch/action/admin/indices/rollover/MaxSizeCondition.java` | ソース | 最大サイズ条件 |
