# バッチ設計書 21-RemoteStorePinnedTimestampService

## 概要

本ドキュメントは、OpenSearchのRemoteStorePinnedTimestampService内部で動作するAsyncUpdatePinnedTimestampTaskバッチ処理の設計を記述する。このバッチはリモートストアのピン留めタイムスタンプ情報を定期的に更新するExperimental機能である。

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

RemoteStorePinnedTimestampServiceは、リモートストア環境におけるタイムスタンプのピン留め管理を行うサービスであり、内部のAsyncUpdatePinnedTimestampTaskが定期的にリモートストアからピン留めタイムスタンプの一覧を取得し、メモリ上のキャッシュを更新する。

**業務上の目的・背景**：リモートストア機能を利用する環境では、スナップショットやガベージコレクションの制御のために特定のタイムスタンプをピン留めする必要がある。ピン留めされたタイムスタンプはリモートストア上のBlobとして管理されるが、各ノードがこの情報を効率的に参照するために、定期的にリモートストアから最新のピン留め情報を取得してローカルのメモリキャッシュを更新する必要がある。この仕組みにより、各ノードは毎回リモートストアへ問い合わせることなくピン留め状態を高速に参照できる。

**バッチの実行タイミング**：サービス起動後、設定値`cluster.remote_store.pinned_timestamps.scheduler_interval`（デフォルト3分、最小1分）の間隔で定期実行される。AbstractAsyncTaskに基づく自動リスケジュール方式である。

**主要な処理内容**：
1. リモートストアのBlobコンテナ（`pinned_timestamps`パス）からBlobメタデータ一覧を取得する
2. 各Blobのファイル名からタイムスタンプ値を解析し、Set<Long>としてピン留めタイムスタンプのセットを構築する
3. 各Blobのファイル名からピン留めエンティティ名を解析し、エンティティ名からタイムスタンプリストへのマッピングを構築する
4. 静的フィールド`pinnedTimestampsSet`および`pinnedEntityToTimestampsMap`を更新する

**前後の処理との関連**：pinTimestamp/unpinTimestampメソッドによるタイムスタンプのピン留め・解除操作がリモートストア上のBlobを変更し、本バッチがそれを読み取って反映する。RemoteSegmentStoreDirectoryやRemoteFsTimestampAwareTranslogがピン留め情報を参照してガベージコレクション判定を行う。

**影響範囲**：リモートストアを使用する全てのノードに影響する。ピン留め情報の更新が遅延すると、ガベージコレクションで必要なセグメントが誤って削除される可能性がある。

## バッチ種別

データ連携（リモートストアからのメタデータ同期）

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 設定可能な間隔で定期実行 |
| 実行時刻 | サービス起動後から継続的に実行 |
| 実行曜日 | 該当なし（常時） |
| 実行日 | 該当なし（常時） |
| トリガー | AbstractAsyncTaskによる自動リスケジュール |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| リモートストア有効 | リモートセグメントストアリポジトリが設定されていること |
| BlobStoreRepository | リポジトリがBlobStoreRepositoryのインスタンスであること |
| サービス起動済み | RemoteStorePinnedTimestampService.start()が呼び出されていること |

### 実行可否判定

AbstractAsyncTaskの`mustReschedule()`は常にtrueを返すため、サービスが起動されclose()されるまで常にリスケジュールされる。

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| cluster.remote_store.pinned_timestamps.scheduler_interval | TimeValue | No | 3m | バッチ実行間隔（最小1分） |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| リモートストアBlobコンテナ（pinned_timestamps） | Blob一覧 | ピン留めタイムスタンプのBlobメタデータ |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| pinnedTimestampsSet（静的フィールド） | Tuple<Long, Set<Long>> | トリガータイムスタンプとピン留めタイムスタンプのセット |
| pinnedEntityToTimestampsMap（静的フィールド） | Map<String, List<Long>> | エンティティ名からタイムスタンプリストへのマッピング |

### 出力ファイル仕様

ファイル出力はなし。メモリ上の静的フィールドのみ更新する。

## 処理フロー

### 処理シーケンス

```
1. トリガータイムスタンプを記録（System.currentTimeMillis()）
   └─ 取得結果と対応づけるためのタイムスタンプ
2. BlobコンテナからBlobメタデータ一覧を取得（blobContainer.listBlobs()）
   └─ pinned_timestampsパス配下の全Blobを列挙
3. Blob一覧が空の場合
   └─ pinnedTimestampsSetを空セットで更新し、pinnedEntityToTimestampsMapを空マップで更新して終了
4. 各BlobファイルからタイムスタンプをパースしSet<Long>を構築
   └─ ファイル名の"__"区切りで最後のトークンをLongとしてパース、-1（パース失敗）はフィルタ
5. pinnedTimestampsSetを更新（トリガータイムスタンプとセットのTuple）
6. 各Blobファイルからエンティティ名を解析しMap<String, List<Long>>を構築
   └─ ファイル名の"__"区切りで最後のトークン以外をエンティティ名として結合
7. pinnedEntityToTimestampsMapを更新
```

### フローチャート

```mermaid
flowchart TD
    A[バッチ開始] --> B[トリガータイムスタンプ記録]
    B --> C[BlobコンテナからBlob一覧取得]
    C --> D{Blob一覧が空?}
    D -->|空| E[空セット・空マップで更新]
    D -->|あり| F[各Blobからタイムスタンプをパース]
    F --> G[pinnedTimestampsSet更新]
    G --> H[各Blobからエンティティマップ構築]
    H --> I[pinnedEntityToTimestampsMap更新]
    E --> J[バッチ終了]
    I --> J
```

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

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

本バッチはデータベースを使用しない。リモートストアのBlobコンテナから読み取り専用でアクセスする。

| 処理 | 対象 | 操作種別 | 概要 |
|-----|------|---------|------|
| Blob一覧取得 | BlobContainer (pinned_timestamps) | READ | ピン留めタイムスタンプBlob一覧を取得 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | NumberFormatException | Blobファイル名のタイムスタンプ部分が不正な場合 | 該当エントリをスキップし、-1としてフィルタ |
| - | Throwable | Blob一覧取得時の任意の例外 | エラーログを出力し、前回の値を保持 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし（次回スケジュールで再実行） |
| リトライ間隔 | スケジューラ間隔（デフォルト3分） |
| リトライ対象エラー | 全てのエラー（次回実行で自動リトライ） |

### 障害時対応

バッチ失敗時はエラーログが出力される。前回取得したピン留めタイムスタンプ情報が保持されるため、即座にデータ喪失には至らない。ただし、長時間更新されない場合はガベージコレクション判定に影響が出る可能性がある。

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | なし（メモリ上の参照置換のみ） |
| コミットタイミング | 該当なし |
| ロールバック条件 | 該当なし（例外時は前回値保持） |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | ピン留めタイムスタンプの数に依存（通常は数十件程度） |
| 目標処理時間 | スケジューラ間隔内に完了すること |
| メモリ使用量上限 | ピン留め数に比例（軽量） |

## 排他制御

静的フィールドの更新は参照の置換で行われるため、スレッドセーフである。ただし、pinnedTimestampsSetとpinnedEntityToTimestampsMapの更新はアトミックではない。

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| DEBUGログ | ピン留めタイムスタンプ取得成功時 | トリガータイムスタンプと取得されたタイムスタンプ一覧 |
| ERRORログ | Blob名のパースエラー時 | 不正なBlob名 |
| ERRORログ | Blob一覧取得の例外時 | 例外詳細 |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| ピン留め情報の更新失敗 | 連続失敗 | OpenSearchログ |
| pinnedTimestampsSetのタイムスタンプ鮮度 | スケジューラ間隔の数倍以上古い場合 | 運用チーム |

## 備考

- 本機能は`@ExperimentalApi`として提供されている
- Blobファイル名の形式は `{pinningEntity}__{timestamp}` であり、`__`が区切り文字として使用される
- forceSyncPinnedTimestamps()メソッドにより、スケジュール外での即座の更新も可能
