# 機能設計書 82-ストアドスクリプト

## 概要

本ドキュメントは、OpenSearchにおけるスクリプトをクラスタ内に保存・管理するストアドスクリプト機能の設計を記述する。

### 本機能の処理概要

ストアドスクリプトは、事前にクラスタのメタデータとしてスクリプトを登録し、IDで参照して再利用できる機能である。検索クエリ、集計、インジェスト等でスクリプトを繰り返し使用する場面において、毎回インラインで記述する代わりに、登録済みスクリプトをIDで呼び出すことができる。

**業務上の目的・背景**：複数のクエリや処理パイプラインで同一のスクリプトロジックを使い回す場合、各リクエストにインラインスクリプトを埋め込むのは冗長で管理が困難である。ストアドスクリプトにより一元管理・再利用が可能となり、スクリプトの変更も一箇所で行える。

**機能の利用シーン**：カスタムスコアリングスクリプトの登録と検索時の参照、インジェストパイプラインで使用するスクリプトの事前登録、テンプレート化された検索クエリの保存などで利用される。

**主要な処理内容**：
1. PUT _scripts/{id} でスクリプトをクラスタメタデータに保存
2. GET _scripts/{id} で保存済みスクリプトを取得
3. DELETE _scripts/{id} で保存済みスクリプトを削除
4. GET _script_language でサポートされるスクリプト言語一覧を取得
5. GET _script_context でスクリプトコンテキスト一覧を取得

**関連システム・外部連携**：ClusterState（クラスタメタデータ）にスクリプトが保存される。ScriptServiceがスクリプトのコンパイル・キャッシュを管理する。

**権限による制御**：スクリプトの登録・削除はクラスタマネージャノードを経由し、METADATA_WRITEレベルのクラスタブロックチェックが行われる。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 139 | スクリプト取得 | 主機能 | 保存済みスクリプトを返す処理 |
| 140 | スクリプト作成・更新 | 主機能 | スクリプトを作成または更新する処理 |
| 141 | スクリプト削除 | 主機能 | スクリプトを削除する処理 |

## 機能種別

CRUD操作（クラスタメタデータの読み書き）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | String | Yes | スクリプトの一意識別子 | 空文字不可、"#"を含めない |
| context | String | No | スクリプトのコンテキスト指定 | 有効なScriptContext名 |
| content | BytesReference | Yes(PUT時) | スクリプト本体（JSON形式） | null不可 |
| mediaType | MediaType | Yes(PUT時) | コンテンツのメディアタイプ | null不可 |
| source | StoredScriptSource | - | パース済みスクリプトソース | 自動生成 |

### 入力データソース

REST API経由のHTTPリクエスト（PUT/GET/DELETE _scripts/{id}）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| _id | String | スクリプトID |
| found | boolean | スクリプトが見つかったか |
| script | StoredScriptSource | スクリプトソース（lang, source, options） |
| acknowledged | boolean | PUT/DELETE操作の成功可否 |

### 出力先

REST APIレスポンスとしてクライアントに返却。スクリプト自体はClusterStateメタデータに永続化される。

## 処理フロー

### 処理シーケンス

```
1. PUT _scripts/{id}: スクリプト登録
   └─ TransportPutStoredScriptAction -> ScriptService.putStoredScript() -> ClusterState更新
2. GET _scripts/{id}: スクリプト取得
   └─ TransportGetStoredScriptAction -> ScriptService.getStoredScript() -> GetStoredScriptResponse
3. DELETE _scripts/{id}: スクリプト削除
   └─ TransportDeleteStoredScriptAction -> ScriptService.deleteStoredScript() -> ClusterState更新
4. GET _script_language: 言語一覧
   └─ TransportGetScriptLanguageAction -> ScriptService.getScriptLanguages()
5. GET _script_context: コンテキスト一覧
   └─ TransportGetScriptContextAction -> ScriptService.getContextInfos()
```

### フローチャート

```mermaid
flowchart TD
    A[REST API リクエスト] --> B{操作種別}
    B -->|PUT| C[TransportPutStoredScriptAction]
    B -->|GET script| D[TransportGetStoredScriptAction]
    B -->|DELETE| E[TransportDeleteStoredScriptAction]
    B -->|GET languages| F[TransportGetScriptLanguageAction]
    C --> G[ClusterBlockチェック METADATA_WRITE]
    G --> H[ScriptService.putStoredScript]
    H --> I[ClusterState更新タスク投入]
    I --> J[AcknowledgedResponse返却]
    D --> K[ScriptService.getStoredScript]
    K --> L[GetStoredScriptResponse返却]
    E --> M[ClusterBlockチェック METADATA_WRITE]
    M --> N[ScriptService.deleteStoredScript]
    N --> O[ClusterState更新タスク投入]
    O --> P[AcknowledgedResponse返却]
    F --> Q[ScriptService.getScriptLanguages]
    Q --> R[GetScriptLanguageResponse返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | ID必須 | スクリプトIDは必須で空文字不可 | PUT/GET/DELETE時 |
| BR-02 | ID制約 | IDに"#"文字を含めることはできない | PUT時 |
| BR-03 | コンテンツ必須 | PUT時にcontent（スクリプト本体）は必須 | PUT時 |
| BR-04 | クラスタマネージャ経由 | PUT/DELETEはクラスタマネージャノードに転送される | PUT/DELETE時 |
| BR-05 | スロットリング | PUT/DELETEタスクはClusterManagerTaskThrottlerで制御される | PUT/DELETE時 |

### 計算ロジック

特になし（CRUD操作のため）。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| PUT | ClusterState.Metadata | UPDATE | スクリプトメタデータの追加・更新 |
| GET | ClusterState.Metadata | SELECT | スクリプトメタデータの読み取り |
| DELETE | ClusterState.Metadata | DELETE | スクリプトメタデータの削除 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 400 | ValidationException | IDが空、"#"を含む、contentがnull | リクエストパラメータを修正 |
| 404 | ResourceNotFoundException | GET時に指定IDのスクリプトが存在しない | スクリプトIDを確認 |
| 409 | ClusterBlockException | METADATA_WRITEブロック中 | クラスタブロックが解除されるまで待機 |

### リトライ仕様

PUT/DELETEはClusterManagerTaskThrottlerにより自動リトライされる。

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

ClusterState更新はアトミックに行われる。更新が失敗した場合はロールバックされる。

## パフォーマンス要件

ストアドスクリプトはClusterStateメタデータに保存されるため、大量のスクリプトを登録するとClusterState更新のオーバーヘッドが増加する。ScriptServiceはコンパイル済みスクリプトをキャッシュする。

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

- METADATA_WRITEレベルのクラスタブロックチェック
- スクリプトの内容はクラスタ内の全ノードに伝播する
- 悪意のあるスクリプトの登録を防ぐため、スクリプト言語ごとのセキュリティ設定が適用される

## 備考

ストアドスクリプトは検索テンプレート（Mustache）の保存にも使用される。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | PutStoredScriptRequest.java | `server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/PutStoredScriptRequest.java` | id, context, content, source(StoredScriptSource)のフィールド構造。validate()メソッド(96-110行目)でバリデーションルールを確認 |
| 1-2 | GetStoredScriptResponse.java | `server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetStoredScriptResponse.java` | _id, found, scriptフィールドの構造。status()で見つからない場合は404を返す(121行目) |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | TransportPutStoredScriptAction.java | `server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportPutStoredScriptAction.java` | TransportClusterManagerNodeActionを継承。clusterManagerOperation()（99-105行目）でScriptService.putStoredScript()に委譲 |
| 2-2 | TransportDeleteStoredScriptAction.java | `server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportDeleteStoredScriptAction.java` | DELETE操作のTransportAction。同様にScriptService.deleteStoredScript()に委譲(99-104行目) |
| 2-3 | TransportGetScriptLanguageAction.java | `server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportGetScriptLanguageAction.java` | HandledTransportActionを継承。doExecute()（58-59行目）でScriptService.getScriptLanguages()を呼出 |

**主要処理フロー**:
1. **85行目(Put)**: putScriptTaskKeyをClusterManagerTaskThrottlerに登録
2. **104行目(Put)**: scriptService.putStoredScript()でクラスタステート更新
3. **108-110行目(Put)**: METADATA_WRITEレベルのブロックチェック

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

```
REST API (PUT/GET/DELETE _scripts/{id})
    |
    +-- TransportPutStoredScriptAction
    |       +-- checkBlock() [METADATA_WRITE]
    |       +-- clusterManagerOperation()
    |               +-- ScriptService.putStoredScript()
    |                       +-- ClusterState更新タスク投入
    |
    +-- TransportDeleteStoredScriptAction
    |       +-- checkBlock() [METADATA_WRITE]
    |       +-- clusterManagerOperation()
    |               +-- ScriptService.deleteStoredScript()
    |
    +-- TransportGetScriptLanguageAction
    |       +-- ScriptService.getScriptLanguages()
    |
    +-- TransportGetScriptContextAction
            +-- ScriptService.getContextInfos()
```

### データフロー図

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

PUT _scripts/{id}         TransportPutStoredScriptAction
  + JSON body  ──────>      + ScriptService ──────────>  ClusterState.Metadata
                                                          + AcknowledgedResponse

GET _scripts/{id} ─────>  ScriptService.getStoredScript ──> GetStoredScriptResponse
                                                              {_id, found, script}

DELETE _scripts/{id} ──>  TransportDeleteStoredScriptAction
                            + ScriptService ──────────>  ClusterState.Metadata
                                                          + AcknowledgedResponse
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| PutStoredScriptAction.java | `server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/PutStoredScriptAction.java` | ソース | PUTアクション定義 |
| PutStoredScriptRequest.java | `server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/PutStoredScriptRequest.java` | ソース | PUTリクエストモデル |
| TransportPutStoredScriptAction.java | `server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportPutStoredScriptAction.java` | ソース | PUTトランスポートアクション |
| GetStoredScriptAction.java | `server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetStoredScriptAction.java` | ソース | GETアクション定義 |
| GetStoredScriptRequest.java | `server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetStoredScriptRequest.java` | ソース | GETリクエストモデル |
| GetStoredScriptResponse.java | `server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetStoredScriptResponse.java` | ソース | GETレスポンスモデル |
| DeleteStoredScriptAction.java | `server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/DeleteStoredScriptAction.java` | ソース | DELETEアクション定義 |
| DeleteStoredScriptRequest.java | `server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/DeleteStoredScriptRequest.java` | ソース | DELETEリクエストモデル |
| TransportDeleteStoredScriptAction.java | `server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportDeleteStoredScriptAction.java` | ソース | DELETEトランスポートアクション |
| TransportGetScriptLanguageAction.java | `server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportGetScriptLanguageAction.java` | ソース | 言語一覧取得アクション |
| TransportGetScriptContextAction.java | `server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportGetScriptContextAction.java` | ソース | コンテキスト一覧取得アクション |
| GetScriptLanguageResponse.java | `server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptLanguageResponse.java` | ソース | 言語一覧レスポンス |
| GetScriptContextResponse.java | `server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptContextResponse.java` | ソース | コンテキスト一覧レスポンス |
