# 機能設計書 54-ノードデコミッション

## 概要

本ドキュメントは、OpenSearchにおけるノードデコミッション機能の設計を記述する。ノードデコミッションは、特定のアウェアネス属性（ゾーン等）を持つノードをクラスタから計画的に除外する機能である。

### 本機能の処理概要

ノードデコミッション機能は、クラスタ内の特定のアウェアネス属性値を持つノードを安全に除外する。デコミッション対象のクラスタマネージャ適格ノードを投票設定から除外し、ドレイニング期間を経て対象ノードをクラスタから除外する。

**業務上の目的・背景**：マルチゾーン構成のOpenSearchクラスタにおいて、特定のゾーンをメンテナンスや障害対応のために計画的に除外する必要がある。デコミッションにより、サービス影響を最小化しながらノードを安全に停止できる。

**機能の利用シーン**：
- 特定のアベイラビリティゾーンのメンテナンス時
- ゾーン障害時の影響ノードの除外
- データセンター移行時の段階的なノード移動
- クラスタのスケールダウン時

**主要な処理内容**：
1. デコミッション属性の登録（ステータス: INIT）
2. 対象ノードの投票設定除外（VotingConfigExclusion）
3. クラスタマネージャの退位待機
4. ドレイニング（ステータス: DRAINING）
5. ノード除外実行（ステータス: IN_PROGRESS）
6. 完了（ステータス: SUCCESSFUL/FAILED）

**関連システム・外部連携**：WeightedRoutingと連携してデコミッション対象の重みを0に設定する必要がある。AllocationServiceと連携してシャードの再配置を行う。

**権限による制御**：
- デコミッション実行: `cluster:admin/decommission/awareness/put`
- デコミッション状態取得: `cluster:admin/decommission/awareness/get`
- リコミッション（解除）: `cluster:admin/decommission/awareness/delete`

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 47 | クラスタヘルス | 参照画面 | デコミッション状態の確認 |
| 58 | 重み付きルーティング | 前提操作 | デコミッション前に重みを0に設定 |

## 機能種別

クラスタ管理 / 運用管理

## 入力仕様

### REST API

#### デコミッション実行
- エンドポイント: `PUT /_cluster/decommission/awareness/{attribute_name}/{attribute_value}`
- アクション名: `cluster:admin/decommission/awareness/put`

#### デコミッション状態取得
- エンドポイント: `GET /_cluster/decommission/awareness/{attribute_name}/_status`
- アクション名: `cluster:admin/decommission/awareness/get`

#### リコミッション（解除）
- エンドポイント: `DELETE /_cluster/decommission/awareness`
- アクション名: `cluster:admin/decommission/awareness/delete`

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| attribute_name | String | Yes | アウェアネス属性名（例: zone） | awareness属性として設定済み |
| attribute_value | String | Yes | デコミッションする属性値（例: zone-1） | - |
| delay_timeout | TimeValue | No | ドレイニング待機時間 | デフォルト120秒 |
| no_delay | boolean | No | ドレイニングをスキップするか | デフォルトfalse |

### 前提条件

- `cluster.routing.allocation.awareness.attributes`に対象属性が設定済み
- `cluster.routing.allocation.awareness.force.{attribute}.values`に対象属性値が設定済み
- 対象属性値の重みが0に設定済み（WeightedRouting）

## 出力仕様

### デコミッション実行レスポンス

| 項目名 | 型 | 説明 |
|--------|-----|------|
| acknowledged | boolean | リクエストが受理されたか |

### デコミッション状態レスポンス

| 項目名 | 型 | 説明 |
|--------|-----|------|
| attribute_name | String | 属性名 |
| attribute_value | String | 属性値 |
| status | String | ステータス（INIT/DRAINING/IN_PROGRESS/SUCCESSFUL/FAILED） |
| request_id | String | リクエストID |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. デコミッションリクエスト受信
   └─ DecommissionService.startDecommissionAction()
2. バリデーション
   └─ アウェアネス属性の検証
   └─ 重みが0に設定されているか検証
3. デコミッション属性登録（INIT）
   └─ ClusterState.metadata.decommissionAttributeMetadataに登録
4. VotingConfigExclusion追加
   └─ 対象のクラスタマネージャ適格ノードを投票から除外
5. クラスタマネージャ退位待機
   └─ 対象ノードがリーダーの場合、退位を待機
6. ドレイニング（DRAINING）
   └─ delay_timeout時間待機
7. ノード除外実行（IN_PROGRESS）
   └─ DecommissionController.removeDecommissionedNodes()
8. 完了（SUCCESSFUL/FAILED）
   └─ VotingConfigExclusionのクリア
```

### フローチャート

```mermaid
flowchart TD
    A[デコミッションリクエスト] --> B{バリデーション}
    B -->|失敗| ERR1[DecommissioningFailedException]
    B -->|成功| C[デコミッション属性登録 INIT]

    C --> D[VotingConfigExclusion追加]
    D --> E{対象がリーダー?}
    E -->|Yes| F[退位待機]
    E -->|No| G[ドレイニング開始]
    F --> G

    G --> H[ステータス更新 DRAINING]
    H --> I[delay_timeout待機]
    I --> J[ステータス更新 IN_PROGRESS]

    J --> K[ノード除外実行]
    K --> L{除外成功?}
    L -->|Yes| M[ステータス SUCCESSFUL]
    L -->|No| N[ステータス FAILED]

    M --> O[VotingConfigExclusionクリア]
    N --> O
    O --> P[完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 重み事前設定 | デコミッション対象の重みは事前に0に設定必須 | 常時 |
| BR-02 | アウェアネス属性必須 | 対象属性はawareness属性として設定済み必須 | 常時 |
| BR-03 | 同時デコミッション禁止 | 同時に複数のデコミッションは不可 | 常時 |
| BR-04 | クォーラム維持 | デコミッション後もクォーラムが維持される必要あり | 常時 |
| BR-05 | ドレイニング期間 | 接続のグレースフル終了のためドレイニング期間を設ける | delay_timeout > 0 |

### DecommissionStatus遷移

```
INIT → DRAINING → IN_PROGRESS → SUCCESSFUL
                              → FAILED
```

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

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

| 操作 | 対象 | 操作種別 | 概要 |
|-----|------|---------|------|
| デコミッション開始 | ClusterState.metadata.decommissionAttributeMetadata | INSERT | デコミッション属性の登録 |
| ステータス更新 | ClusterState.metadata.decommissionAttributeMetadata | UPDATE | ステータス遷移 |
| VotingConfig除外 | ClusterState.coordinationMetadata.votingConfigExclusions | UPDATE | 投票除外の追加/削除 |
| ノード除外 | ClusterState.nodes | UPDATE | ノード一覧から削除 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 400 | DecommissioningFailedException | アウェアネス属性未設定 | 属性を設定する |
| 400 | DecommissioningFailedException | 重みが0でない | 重みを0に設定する |
| 400 | DecommissioningFailedException | 既にデコミッション中 | 完了を待つかリコミッションする |
| 408 | OpenSearchTimeoutException | 退位タイムアウト | リトライまたはリコミッション |

### リトライ仕様

デコミッション処理は内部で自動リトライを行う。失敗した場合はステータスがFAILEDになり、リコミッション後に再実行可能。

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

デコミッションはClusterStateUpdateTaskとして実行される。各ステータス遷移はアトミックに行われる。失敗時はステータスがFAILEDに設定される。

## パフォーマンス要件

- デコミッション処理はクラスタマネージャノードで実行
- ドレイニング期間はdelay_timeout（デフォルト120秒）
- 大量のシャードがある場合、再配置に時間がかかる可能性

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

- デコミッション実行には`cluster:admin/decommission/awareness/put`権限が必要
- リコミッションには`cluster:admin/decommission/awareness/delete`権限が必要
- 誤操作防止のため、重みの事前設定を必須としている

## 備考

- V2.4.0以降で利用可能
- リコミッション（DELETE）により、デコミッション状態をクリアしてノードの再参加を許可
- 単一ゾーンのデコミッションは、クォーラム維持のため半数以下のノードに限定される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | DecommissionAttribute.java | `server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java` | デコミッション属性のデータ構造 |
| 1-2 | DecommissionAttributeMetadata.java | `server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java` | クラスタメタデータとしての属性情報 |
| 1-3 | DecommissionStatus.java | `server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java` | ステータス列挙型（INIT/DRAINING/IN_PROGRESS/SUCCESSFUL/FAILED） |

#### Step 2: サービスクラスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | DecommissionService.java | `server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java` | デコミッションのメインサービス。startDecommissionAction()、drainNodesWithDecommissionedAttribute()を確認 |

**主要処理フロー**:
- **135-300行目**: startDecommissionAction() -- デコミッション開始処理
- **304-342行目**: drainNodesWithDecommissionedAttribute() -- ドレイニング処理
- **365-412行目**: failDecommissionedNodes() -- ノード除外処理
- **540-571行目**: startRecommissionAction() -- リコミッション処理

#### Step 3: コントローラを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | DecommissionController.java | `server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java` | ノード除外の実行処理 |
| 3-2 | DecommissionHelper.java | `server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java` | ユーティリティメソッド群 |

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

```
REST API (PUT /_cluster/decommission/awareness/{attr}/{value})
    |
    +-- DecommissionService.startDecommissionAction()
          |
          +-- validateAwarenessAttribute() -- 属性検証
          |
          +-- ensureEligibleRequest() -- リクエスト適格性検証
          |
          +-- ensureToBeDecommissionedAttributeWeighedAway() -- 重み検証
          |
          +-- registerDecommissionAttributeInClusterState() -- INIT登録
          |
          +-- addVotingConfigExclusionsForNodesToBeDecommissioned()
          |
          +-- [ClusterStateObserver] -- 退位待機
          |
          +-- drainNodesWithDecommissionedAttribute()
          |     +-- updateMetadataWithDecommissionStatus(DRAINING)
          |     +-- scheduleNodesDecommissionOnTimeout()
          |
          +-- failDecommissionedNodes()
                +-- updateMetadataWithDecommissionStatus(IN_PROGRESS)
                +-- DecommissionController.removeDecommissionedNodes()
                +-- updateMetadataWithDecommissionStatus(SUCCESSFUL/FAILED)
```

### データフロー図

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

DecommissionRequest -----> DecommissionService -----------------> DecommissionResponse
 (attribute_name,           |                                      (acknowledged)
  attribute_value,          +-> validateAwarenessAttribute()
  delay_timeout)            +-> ensureEligibleRequest()
                            +-> registerDecommissionAttribute()
                            +-> addVotingConfigExclusions()
                            +-> drainNodes()
                            +-> DecommissionController.removeNodes()
                            |
                            +-> ClusterState更新
                                 (decommissionAttributeMetadata,
                                  votingConfigExclusions,
                                  nodes)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| DecommissionService.java | `server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java` | ソース | メインサービス |
| DecommissionController.java | `server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java` | ソース | ノード除外コントローラ |
| DecommissionHelper.java | `server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java` | ソース | ヘルパーユーティリティ |
| DecommissionAttribute.java | `server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java` | ソース | 属性データ構造 |
| DecommissionAttributeMetadata.java | `server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java` | ソース | メタデータ |
| DecommissionStatus.java | `server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java` | ソース | ステータス列挙型 |
| DecommissioningFailedException.java | `server/src/main/java/org/opensearch/cluster/decommission/DecommissioningFailedException.java` | ソース | 例外クラス |
| NodeDecommissionedException.java | `server/src/main/java/org/opensearch/cluster/decommission/NodeDecommissionedException.java` | ソース | ノード除外済み例外 |
