# 画面設計書 102-重み付きルーティング設定

## 概要

本ドキュメントは、OpenSearchクラスタにおける重み付きシャードルーティングの重みを設定・更新するREST APIエンドポイント `PUT /_cluster/routing/awareness/{attribute}/weights` の設計を記述する。

### 本画面の処理概要

本APIは、Weighted Round-Robin方式のシャードルーティングポリシーにおける各ゾーンの重みを設定・更新するためのエンドポイントである。

**業務上の目的・背景**：マルチゾーン構成のOpenSearchクラスタにおいて、特定ゾーンへの検索トラフィックの比率を制御するために重み付きルーティングを設定する。例えば、あるゾーンのノードが高負荷状態の場合にそのゾーンの重みを0にしてトラフィックを遮断したり、ゾーン間の重みを調整してリクエスト分散を最適化する。障害時のトラフィック制御やカナリアデプロイメントのトラフィックシフトにも活用される。

**画面へのアクセス方法**：HTTPクライアントから `PUT /_cluster/routing/awareness/{attribute}/weights` エンドポイントにPUTリクエストを送信する。リクエストボディにJSON形式で重みを指定する。

**主要な操作・処理内容**：
1. クライアントからPUTリクエストを受信し、URLパスパラメータからアウェアネス属性名を抽出する
2. リクエストボディからゾーン名と重み値のマッピング、およびバージョン番号を解析する
3. バリデーション（属性名、重み値、バージョン、ゼロ重みの数制約）を実行する
4. クラスタマネージャノードでクラスタ状態メタデータを更新する

**画面遷移**：関連するAPIとして、重みを取得する `GET /_cluster/routing/awareness/{attribute}/weights`（No.101）、重みを削除する `DELETE /_cluster/routing/awareness/weights`（No.103）がある。

**権限による表示制御**：クラスタレベルのメタデータ書き込み権限が必要である。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 58 | 重み付きルーティング | 主機能 | 重み付きシャードルーティングの重みを更新する処理 |

## 画面種別

設定更新（PUT）API

## URL/ルーティング

| メソッド | パス | 説明 |
|---------|------|------|
| PUT | `/_cluster/routing/awareness/{attribute}/weights` | 重み付きルーティングの重みを設定・更新 |

## 入出力項目

### パスパラメータ

| パラメータ名 | 型 | 必須 | 説明 |
|-------------|-----|------|------|
| attribute | string | Yes | アウェアネス属性名（例: `zone`） |

### リクエストボディ

| フィールド | 型 | 必須 | 説明 |
|-----------|-----|------|------|
| weights | object | Yes | ゾーン名をキー、重み値（文字列形式のDouble）を値とするマップ |
| _version | long | Yes | 楽観的排他制御のためのバージョン番号 |

### リクエスト例

```json
{
  "weights": {
    "zone-a": "1.0",
    "zone-b": "1.0",
    "zone-c": "0.0"
  },
  "_version": 3
}
```

## 表示項目

### レスポンスボディ

| フィールド | 型 | 説明 |
|-----------|-----|------|
| acknowledged | boolean | リクエストが受理されたかどうか |

## イベント仕様

### 1-PUTリクエスト受信

1. `RestClusterPutWeightedRoutingAction.prepareRequest()` がリクエストを受信する
2. `createRequest()` メソッドで `ClusterPutWeightedRoutingRequest` を生成する
3. URLパスから `attribute` パラメータを抽出する
4. リクエストボディを解析して `WeightedRouting` オブジェクトを構築する
5. バリデーション処理が実行される
6. `NodeClient` 経由で `TransportAddWeightedRoutingAction` にリクエストを転送する
7. クラスタマネージャノードでクラスタ状態メタデータが更新される

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| PUTリクエスト | ClusterState Metadata | UPDATE | WeightedRoutingMetadataの更新 |

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

#### ClusterState Metadata

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | WeightedRoutingMetadata.weightedRouting | リクエストボディのweightsマッピング | ゾーン名と重みの組み合わせ |
| UPDATE | WeightedRoutingMetadata.version | リクエストボディの_version + 1 | 楽観的排他制御に使用 |

## メッセージ仕様

| 種別 | メッセージ | 条件 |
|------|----------|------|
| エラー | "Weighted routing request object is null" | リクエストオブジェクトがnullの場合 |
| エラー | "Attribute name is missing" | アウェアネス属性名が未指定の場合 |
| エラー | "Weights are missing" | 重みマッピングが空の場合 |
| エラー | "Version is missing" | バージョン番号が未指定の場合 |
| エラー | "Weight is null" | 重み値がnullの場合 |
| エラー | "Weight is not a number" | 重み値が数値でない場合 |
| エラー | "There are too many attribute values [...] given zero weight [...]" | ゼロ重みの数がゾーン数の半分を超える場合 |
| 成功 | 200 OK + `{"acknowledged": true}` | 正常に更新できた場合 |

## 例外処理

| 例外条件 | HTTPステータス | レスポンス内容 |
|---------|---------------|---------------|
| バリデーションエラー | 400 Bad Request | 各バリデーションエラーメッセージ |
| バージョン不一致 | 409 Conflict | 楽観的排他制御によるエラー |
| クラスタブロック中 | 403 Forbidden | ClusterBlockException |
| 空のリクエストボディ | 400 Bad Request | "Empty request body" |

## 備考

- 本APIはOpenSearch 2.4.0以降で利用可能
- 楽観的排他制御のため、`_version` フィールドは必須。現在のバージョンは `GET` APIで取得できる
- ゼロ重みに設定できるゾーン数は、総ゾーン数の半分以下に制限されている。これはクラスタの可用性を保証するための制約である
- リクエストボディの解析は `ClusterPutWeightedRoutingRequest.setWeightedRouting()` メソッドで行われる

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ClusterPutWeightedRoutingRequest.java | `server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/put/ClusterPutWeightedRoutingRequest.java` | リクエスト構造。`setWeightedRouting()` メソッド（行84-163）でボディ解析、`validate()` メソッド（行166-211）でバリデーション |
| 1-2 | WeightedRouting.java | `server/src/main/java/org/opensearch/cluster/routing/WeightedRouting.java` | 属性名と重みマップ `Map<String, Double>` |

**読解のコツ**: `validate()` メソッドの制約条件（ゼロ重みの数 <= 総数/2）は運用上の重要なルールである。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | RestClusterPutWeightedRoutingAction.java | `server/src/main/java/org/opensearch/rest/action/admin/cluster/RestClusterPutWeightedRoutingAction.java` | ルート定義（行38）と `createRequest()` メソッド（行52-56） |

**主要処理フロー**:
1. **行38**: `PUT /_cluster/routing/awareness/{attribute}/weights` のルートを登録
2. **行48**: `createRequest()` でリクエストオブジェクトを生成
3. **行53**: パスパラメータ `attribute` を取得
4. **行54**: `request.applyContentParser()` でリクエストボディを解析

#### Step 3: Transport層の処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | TransportAddWeightedRoutingAction.java | `server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/put/TransportAddWeightedRoutingAction.java` | クラスタ状態メタデータの更新ロジック |

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

```
RestClusterPutWeightedRoutingAction.prepareRequest()
    |
    +-- createRequest(RestRequest)
    |       +-- Requests.putWeightedRoutingRequest(attribute)
    |       +-- request.applyContentParser() --> ClusterPutWeightedRoutingRequest.source()
    |               +-- setWeightedRouting(Map<String, Object>)
    |
    +-- NodeClient.admin().cluster().putWeightedRouting()
            |
            +-- TransportAddWeightedRoutingAction.clusterManagerOperation()
                    +-- WeightedRoutingService.registerWeightedRoutingMetadata()
```

### データフロー図

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

PUT request              --> RestClusterPutWeightedRoutingAction --> ClusterPutWeightedRoutingRequest
  attribute param            .createRequest()
  JSON body (weights,
  _version)

                         --> TransportAddWeightedRoutingAction   --> ClusterStateUpdateResponse
                             .clusterManagerOperation()               { acknowledged: true }
                             WeightedRoutingService
                               .registerWeightedRoutingMetadata()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| RestClusterPutWeightedRoutingAction.java | `server/src/main/java/org/opensearch/rest/action/admin/cluster/RestClusterPutWeightedRoutingAction.java` | ソース | RESTエンドポイント定義 |
| ClusterPutWeightedRoutingRequest.java | `server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/put/ClusterPutWeightedRoutingRequest.java` | ソース | リクエスト・バリデーション |
| ClusterAddWeightedRoutingAction.java | `server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/put/ClusterAddWeightedRoutingAction.java` | ソース | アクション名定義 |
| TransportAddWeightedRoutingAction.java | `server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/put/TransportAddWeightedRoutingAction.java` | ソース | Transport層ビジネスロジック |
| WeightedRoutingMetadata.java | `server/src/main/java/org/opensearch/cluster/metadata/WeightedRoutingMetadata.java` | ソース | メタデータ保持 |
| WeightedRoutingService.java | `server/src/main/java/org/opensearch/cluster/routing/WeightedRoutingService.java` | ソース | 重み付きルーティングサービス |
| cluster.put_weighted_routing.json | `rest-api-spec/src/main/resources/rest-api-spec/api/cluster.put_weighted_routing.json` | 設定 | REST APIスペック定義 |
