# バッチ設計書 19-RemoteClusterStateCleanupManager (AsyncStaleFileDeletion)

## 概要

本ドキュメントは、リモートストアの古いクラスタ状態ファイルを定期的に削除するバッチ処理「RemoteClusterStateCleanupManager (AsyncStaleFileDeletion)」の設計仕様を記載する。

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

RemoteClusterStateCleanupManagerは、リモートストア（BlobStore）に保存されたクラスタ状態のマニフェストおよびメタデータファイルのうち、古くなった不要ファイルを定期的に検出・削除するバッチ処理である。内部のAsyncStaleFileDeletionタスクがAbstractAsyncTaskを継承して定期実行される。

**業務上の目的・背景**：OpenSearchのリモートストア機能では、クラスタ状態（マニフェスト、インデックスメタデータ、グローバルメタデータ、ルーティングテーブル等）がリモートBlobStoreに永続化される。クラスタ状態が更新されるたびに新しいファイルが作成されるため、古いバージョンのファイルが蓄積し、ストレージコストが増大する。本バッチは、最新10件のマニフェストを保持しつつ、それ以前のマニフェストと関連する未参照メタデータファイルを削除することで、ストレージ使用量を適正に保つ。

**バッチの実行タイミング**：`cluster.remote_store.state.cleanup_interval`設定に基づく定期実行（デフォルト5分間隔）。start()メソッドでAsyncStaleFileDeletionタスクが作成・開始される。

**主要な処理内容**：
1. ローカルノードがクラスタマネージャであることを確認
2. 前回クリーンアップからのクラスタ状態変更数が閾値（10回）を超えているか確認
3. マニフェスト一覧を取得し、最新10件を保持対象として特定
4. 保持対象マニフェストが参照するファイルをfilesToKeepセットに登録
5. 古いマニフェストが参照するファイルのうち、filesToKeepに含まれないものを削除対象として特定
6. インデックスメタデータ、グローバルメタデータ、ルーティングテーブル、エフェメラル属性等を分類して削除

**前後の処理との関連**：RemoteClusterStateServiceがクラスタ状態の永続化を担当し、本バッチがその古いファイルのクリーンアップを行う。RemoteRoutingTableServiceと連携してルーティングテーブルファイルの削除も行う。クラスタマネージャ選出後にstart()が呼ばれ、動作を開始する。

**影響範囲**：リモートBlobStoreのストレージ使用量に影響。最新10件のマニフェストとそれが参照するファイルは保持されるため、クラスタの運用に影響はない。

## バッチ種別

クラスタ管理 / ストレージクリーンアップ

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 定期実行（デフォルト5分間隔） |
| 実行時刻 | クラスタマネージャ選出後、設定間隔ごとに繰り返し実行 |
| 実行曜日 | 該当なし（常時） |
| 実行日 | 該当なし（常時） |
| トリガー | AbstractAsyncTask（REMOTE_PURGEスレッドプール） |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| ローカルノードがクラスタマネージャであること | isLocalNodeElectedClusterManager()で確認 |
| リモートストアが有効であること | RemoteClusterStateServiceが初期化済み |
| クラスタ名とUUIDが設定されていること | assertで検証 |

### 実行可否判定

1. ローカルノードがクラスタマネージャでない場合はスキップ
2. 前回クリーンアップからのクラスタ状態バージョン差分がSKIP_CLEANUP_STATE_CHANGES（10）以下の場合はスキップ
3. deleteStaleMetadataRunningフラグ（AtomicBoolean）がすでにtrueの場合はスキップ（排他制御）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| cluster.remote_store.state.cleanup_interval | TimeValue | No | 5m | クリーンアップ間隔（最小値: -1で無効化） |
| RETAINED_MANIFESTS | int | - | 10 | 保持するマニフェスト数（コード内定数） |
| SKIP_CLEANUP_STATE_CHANGES | int | - | 10 | クリーンアップをスキップする状態変更回数（コード内定数） |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| BlobStore（マニフェスト） | リモートストア | クラスタ状態マニフェストファイル一覧 |
| ClusterApplierService.state() | クラスタ状態 | 現在適用されたクラスタ状態（バージョン、UUID等） |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| BlobStore | ファイル削除 | 古いマニフェスト、インデックスメタデータ、グローバルメタデータ、ルーティングテーブル等の削除 |
| RemotePersistenceStats | 統計更新 | クリーンアップ成功/失敗の統計 |

### 出力ファイル仕様

ファイル出力はなし（リモートストアからのファイル削除処理）。

## 処理フロー

### 処理シーケンス

```
1. AsyncStaleFileDeletion.runInternal()実行
   └─ cleanUpStaleFiles()を呼び出し
2. cleanUpStaleFiles()
   ├─ クラスタマネージャ判定
   ├─ クラスタ状態バージョン差分チェック（> SKIP_CLEANUP_STATE_CHANGES）
   └─ deleteStaleClusterMetadata()を呼び出し
3. deleteStaleClusterMetadata()
   ├─ deleteStaleMetadataRunningフラグをtrue設定（CAS）
   ├─ listAllInSortedOrderAsync()でマニフェスト一覧取得
   ├─ マニフェスト数 > manifestsToRetainの場合にdeleteClusterMetadata()呼び出し
   └─ deleteStaleMetadataRunningフラグをfalse設定
4. deleteClusterMetadata()
   ├─ アクティブマニフェストからfilesToKeepを構築
   ├─ 古いマニフェストからstaleファイルを分類
   │   ├─ staleManifestPaths
   │   ├─ staleIndexMetadataPaths
   │   ├─ staleGlobalMetadataPaths
   │   ├─ staleEphemeralAttributePaths
   │   ├─ staleIndexRoutingPaths
   │   └─ staleIndexRoutingDiffPaths
   ├─ 各カテゴリのファイルを削除
   └─ エラーハンドリング・統計更新
```

### フローチャート

```mermaid
flowchart TD
    A[AsyncStaleFileDeletion実行] --> B{クラスタマネージャ?}
    B -->|No| C[スキップ]
    B -->|Yes| D{バージョン差分 > 10?}
    D -->|No| E[スキップ: デバッグログ]
    D -->|Yes| F{deleteStaleMetadataRunning?}
    F -->|Yes| G[スキップ: 実行中]
    F -->|No| H[マニフェスト一覧取得]
    H --> I{マニフェスト数 > 10?}
    I -->|No| J[削除不要]
    I -->|Yes| K[filesToKeep構築]
    K --> L[staleファイル分類]
    L --> M[グローバルメタデータ削除]
    M --> N[インデックスメタデータ削除]
    N --> O[エフェメラル属性削除]
    O --> P[マニフェスト削除]
    P --> Q[ルーティングテーブル削除]
    Q --> R[フラグリセット]
```

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

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

本バッチはリモートBlobStoreに対するファイル削除操作を行う。

| 処理 | 対象 | 操作種別 | 概要 |
|-----|------|---------|------|
| マニフェスト削除 | BlobStore | DELETE | 古いマニフェストファイルの削除 |
| インデックスメタデータ削除 | BlobStore | DELETE | 未参照インデックスメタデータの削除 |
| グローバルメタデータ削除 | BlobStore | DELETE | 未参照グローバルメタデータの削除 |
| ルーティングテーブル削除 | BlobStore | DELETE | 未参照ルーティングテーブルの削除 |
| エフェメラル属性削除 | BlobStore | DELETE | 未参照エフェメラル属性の削除 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| なし | IllegalStateException | マニフェスト取得時の不正状態 | エラーログ出力 |
| なし | IOException | ファイル削除失敗 | エラーログ出力、cleanUpAttemptFailed統計を更新 |
| なし | IOException | ルーティングファイル削除失敗 | エラーログ出力、indexRoutingFilesCleanupAttemptFailed統計を更新 |
| なし | Exception | マニフェスト一覧取得失敗 | エラーログ出力、deleteStaleMetadataRunningフラグをリセット |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 明示的なリトライなし（定期実行により自動再試行） |
| リトライ間隔 | 次回定期実行間隔（デフォルト5分） |
| リトライ対象エラー | すべてのException |

### 障害時対応

ファイル削除に失敗した場合、RemotePersistenceStatsにエラー統計が記録される。次回実行時に再度削除が試みられる。ストレージ使用量が異常に増加する場合は、手動でリモートストアのクリーンアップを検討する。

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | なし（各ファイル削除は独立） |
| コミットタイミング | 各削除操作完了時 |
| ロールバック条件 | なし（部分的な削除は許容される） |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | マニフェスト数に依存（古いマニフェスト数 x 参照ファイル数） |
| 目標処理時間 | リモートストアのレイテンシに依存 |
| メモリ使用量上限 | マニフェスト数 x メタデータパスのセットサイズ |

## 排他制御

deleteStaleMetadataRunning（AtomicBoolean）により、同時に複数のクリーンアップ処理が実行されないよう制御される。compareAndSetでtrueに設定し、処理完了時（成功・失敗問わず）にfalseに戻す。

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| 情報ログ | クリーンアップ開始時 | "Cleaning up stale remote state files for cluster [{name}] with uuid [{uuid}]" |
| デバッグログ | スキップ時 | "Skipping cleanup of stale remote state files..." |
| デバッグログ | クラスタマネージャでない場合 | "Skipping cleanup task as local node is not elected Cluster Manager" |
| 情報ログ | 既に実行中の場合 | "Delete stale cluster metadata task is already in progress." |
| デバッグログ | 削除対象なしの場合 | "No stale Remote Cluster Metadata files found" |
| エラーログ | マニフェスト取得失敗時 | "Error while fetching Remote Cluster Metadata manifests" |
| エラーログ | ファイル削除失敗時 | "Error while deleting stale Remote Cluster Metadata files" |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| クリーンアップ失敗回数 | RemotePersistenceStats.cleanUpAttemptFailed | 統計API |
| ルーティングファイル削除失敗 | RemotePersistenceStats.indexRoutingFilesCleanupAttemptFailed | 統計API |

## 備考

- 実装ファイル: `server/src/main/java/org/opensearch/gateway/remote/RemoteClusterStateCleanupManager.java`
- AsyncStaleFileDeletionはAbstractAsyncTaskを継承した内部クラス
- REMOTE_PURGEスレッドプールで実行される
- RETAINED_MANIFESTS=10、SKIP_CLEANUP_STATE_CHANGES=10はコード内定数
- cluster.remote_store.state.cleanup_intervalを-1に設定すると無効化可能
- 設定変更時はsetInterval()で動的にタスクの間隔が更新される
- クラスタUUIDの削除（deleteStaleUUIDsClusterMetadata）は、直近2つのUUIDを保持する
- Closeableインターフェースを実装しており、close()でAsyncStaleFileDeletionタスクを停止する
