# 帳票設計書 11-Fielddata Report

## 概要

本ドキュメントは、OpenSearchのCat API「/_cat/fielddata」エンドポイントが出力するFielddata Reportの設計仕様を定義する。本帳票はクラスタ内の各データノードにおけるフィールドデータのメモリ使用量を、フィールド単位でテキストテーブル形式にて出力するものである。

### 本帳票の処理概要

本帳票は、OpenSearchクラスタのデータノードに蓄積されたフィールドデータ（fielddata）のメモリ消費状況を可視化する。フィールドデータはソート・集約・スクリプト処理において使用されるインメモリデータ構造であり、その使用量を把握することはクラスタのメモリ管理において不可欠である。

**業務上の目的・背景**：フィールドデータはヒープメモリを大量に消費する可能性があり、適切に管理しなければOutOfMemoryエラーやサーキットブレーカーの発動を招く。本帳票によりノードごと・フィールドごとのメモリ使用量を迅速に把握し、メモリ圧迫の原因となるフィールドを特定できる。

**帳票の利用シーン**：クラスタのメモリ使用量が高騰した際の原因調査、定期的なメモリ使用状況の監視、特定フィールドのfielddata使用量の確認、fielddata cache evictionが頻発する場合のトラブルシューティングに利用される。

**主要な出力内容**：
1. ノード識別情報（ノードID、ホスト名、IPアドレス、ノード名）
2. フィールド名（fielddata対象のフィールド）
3. フィールドデータサイズ（バイト単位のメモリ使用量）

**帳票の出力タイミング**：ユーザーがREST APIエンドポイント `GET /_cat/fielddata` または `GET /_cat/fielddata/{fields}` にHTTPリクエストを送信した際にリアルタイムで生成・出力される。

**帳票の利用者**：OpenSearchクラスタ管理者、SRE（Site Reliability Engineer）、インフラストラクチャエンジニア、パフォーマンスチューニング担当者。

## 帳票種別

一覧表（ノード別フィールドデータ使用量一覧）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | REST API | `GET /_cat/fielddata` | HTTPリクエスト送信 |
| - | REST API | `GET /_cat/fielddata/{fields}` | HTTPリクエスト送信（フィールド指定） |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | text/plain（テキストテーブル）またはJSON/YAML/CBOR/Smile（format指定時） |
| 用紙サイズ | N/A（API応答） |
| 向き | N/A |
| ファイル名 | N/A（HTTPレスポンスボディ） |
| 出力方法 | HTTPレスポンス |
| 文字コード | UTF-8 |

### PDF固有設定

該当なし（API応答のため）

### Excel固有設定

該当なし（API応答のため）

## 帳票レイアウト

### レイアウト概要

テキストテーブル形式で、1行がノードの1フィールドに対応するフラットな一覧を出力する。ヘッダー行にカラム名が表示され、その下にデータ行が続く。

```
┌────────────────────────────────────────────────────┐
│  ヘッダー部（カラム名: id, host, ip, node,         │
│             field, size）                          │
├────────────────────────────────────────────────────┤
│  明細部（ノード×フィールドごとに1行）                 │
│  例: nodeId1  host1  ip1  node1  field1  10.5kb   │
│      nodeId1  host1  ip1  node1  field2  5.2kb    │
│      nodeId2  host2  ip2  node2  field1  8.3kb    │
└────────────────────────────────────────────────────┘
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | id | ノードID | NodeStats.getNode().getId() | 文字列 |
| 2 | host | ホスト名 | NodeStats.getNode().getHostName() | 文字列（alias: h） |
| 3 | ip | IPアドレス | NodeStats.getNode().getHostAddress() | 文字列 |
| 4 | node | ノード名 | NodeStats.getNode().getName() | 文字列（alias: n） |
| 5 | field | フィールド名 | FieldData.getFields()のキー | 文字列（alias: f） |
| 6 | size | フィールドデータ使用量 | FieldData.getFields()の値 | ByteSizeValue（alias: s、右寄せ） |

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | id | ノードID | NodeStats.getNode().getId() | 文字列 | 可変 |
| 2 | host | ホスト名 | NodeStats.getNode().getHostName() | 文字列 | 可変 |
| 3 | ip | IPアドレス | NodeStats.getNode().getHostAddress() | 文字列 | 可変 |
| 4 | node | ノード名 | NodeStats.getNode().getName() | 文字列 | 可変 |
| 5 | field | フィールド名 | cursor.getKey() | 文字列 | 可変 |
| 6 | size | フィールドデータ使用量 | new ByteSizeValue(cursor.getValue()) | バイトサイズ（人間可読形式） | 可変 |

### フッター部

フッター部は存在しない。

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| ノードフィルタ | データノード（data:true）のみを対象 | Yes（固定） |
| フィールドフィルタ | URLパスパラメータ {fields} で指定されたフィールドのみ出力。未指定時は全フィールド（*） | No |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | ノード順 | NodesStatsResponseの返却順 |
| 2 | フィールド順 | FieldDataのイテレーション順 |

### 改ページ条件

改ページなし（全件を1レスポンスで返却）。

## データベース参照仕様

### 参照テーブル一覧

本帳票はデータベーステーブルを直接参照しない。クラスタ内部のノード統計情報をAPIで取得する。

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| NodesStats（内部API） | ノード単位のフィールドデータ使用量取得 | NodesStatsRequest("data:true") |

### テーブル別参照項目詳細

#### NodesStatsResponse

| 参照項目（カラム名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| NodeStats.getNode().getId() | id | - | ノード一意識別子 |
| NodeStats.getNode().getHostName() | host | - | - |
| NodeStats.getNode().getHostAddress() | ip | - | - |
| NodeStats.getNode().getName() | node | - | - |
| NodeStats.getIndices().getFieldData().getFields() | field, size | fieldDataFields指定 | Map形式（キー:フィールド名、値:バイト数） |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| size | new ByteSizeValue(cursor.getValue()) | ByteSizeValueの自動変換 | バイト数を人間可読形式（kb, mb, gb等）に変換 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[クライアントからGETリクエスト受信] --> B[RestFielddataAction.doCatRequest]
    B --> C[NodesStatsRequest生成 - data:trueノード指定]
    C --> D[indices=true, fieldDataFields設定]
    D --> E[NodeClient.admin.cluster.nodesStats実行]
    E --> F[NodesStatsResponse受信]
    F --> G[buildTable: ノードごとにループ]
    G --> H[各ノードのFieldData.getFields取得]
    H --> I[フィールドごとにテーブル行追加]
    I --> J[RestTable.buildResponse]
    J --> K[HTTPレスポンス返却]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| データなし | フィールドデータが存在しないノード | 該当ノード・フィールドの行が出力されない（空テーブル） | getFields()がnullの場合はスキップ（行108） |
| ノード応答なし | データノードが応答しない場合 | NodesStatsResponseのfailures要素に記録 | タイムアウト等はフレームワーク側で処理 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | ノード数 x フィールド数（典型的には数十〜数百行） |
| 目標出力時間 | ノード統計収集に依存（通常1秒以内） |
| 同時出力数上限 | REST APIスレッドプールに依存 |

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

本帳票はクラスタの内部統計情報を公開するため、適切なアクセス制御が必要である。OpenSearch Securityプラグインを通じたロールベースアクセス制御（RBAC）により、cluster:monitor/nodes/stats権限を持つユーザーのみがアクセス可能とすべきである。

## 備考

- `?v` パラメータを付与するとヘッダー行が出力される
- `?h=field,size` のようにカラムを指定して出力を絞り込める
- `?format=json` でJSON形式、`?format=yaml` でYAML形式の出力が可能
- `?help` パラメータで各カラムの説明が表示される
- `?s=size:desc` でサイズの降順ソートが可能
- `?bytes=b` でバイト単位の数値出力が可能

---

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

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

### 推奨読解順序

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

フィールドデータ統計情報のデータ構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | NodesStatsResponse.java | `server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodesStatsResponse.java` | ノード統計レスポンスの構造 |
| 1-2 | NodeStats.java | `server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodeStats.java` | 個別ノード統計の構造。getIndices()でインデックス統計へアクセス |
| 1-3 | Table.java | `server/src/main/java/org/opensearch/common/Table.java` | テーブルデータ構造。startHeaders/addCell/startRow/endRowの使い方 |

**読解のコツ**: NodesStatsResponseはBaseNodesResponseを継承し、複数のNodeStatsを保持するリスト構造である。

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

RESTリクエストの受け口を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | RestFielddataAction.java | `server/src/main/java/org/opensearch/rest/action/cat/RestFielddataAction.java` | 帳票のメインクラス。全124行と短い |

**主要処理フロー**:
1. **行59-61**: routes()メソッドで `/_cat/fielddata` と `/_cat/fielddata/{fields}` の2つのルートを定義
2. **行69-82**: doCatRequest()で NodesStatsRequest を生成し、data:trueノードのfielddata統計を要求
3. **行70-74**: fields パラメータの解析。未指定時は "*"（全フィールド）
4. **行91-101**: getTableWithHeader()でテーブルヘッダーを定義（id, host, ip, node, field, size の6カラム）
5. **行104-123**: buildTable()で応答データからテーブル行を組み立て

#### Step 3: 基底クラスを理解する

Cat APIの共通処理フレームワークを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | AbstractCatAction.java | `server/src/main/java/org/opensearch/rest/action/cat/AbstractCatAction.java` | prepareRequest()でhelpパラメータ処理とdoCatRequest()へのディスパッチ |
| 3-2 | RestTable.java | `server/src/main/java/org/opensearch/rest/action/cat/RestTable.java` | buildResponse()でテキスト/JSON/YAML形式の出力を決定 |

**主要処理フロー**:
- **AbstractCatAction 行68-91**: helpパラメータの有無で分岐。help=trueならカラム説明を出力、falseならdoCatRequest()を呼出
- **RestTable 行73-79**: format指定によりXContent形式またはテキスト形式でレスポンスを構築

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

```
HTTP GET /_cat/fielddata
    |
    +-- AbstractCatAction.prepareRequest() [行68]
        |
        +-- RestFielddataAction.doCatRequest() [行69]
            |
            +-- NodesStatsRequest("data:true") [行70]
            |   +-- .clear() / .indices(true) [行71-72]
            |   +-- .indices().fieldDataFields(fields) [行74]
            |
            +-- NodeClient.admin().cluster().nodesStats() [行76]
                |
                +-- RestResponseListener.buildResponse() [行78]
                    |
                    +-- RestFielddataAction.buildTable() [行104]
                    |   +-- getTableWithHeader() [行91]
                    |   +-- NodeStats loop [行107]
                    |       +-- FieldData.getFields() loop [行109]
                    |           +-- table.addCell() x 6 [行111-116]
                    |
                    +-- RestTable.buildResponse() [行79]
```

### データフロー図

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

HTTP GET Request   --> RestFielddataAction           --> HTTP Response
  /_cat/fielddata       |                                 (text/plain
  ?fields=xxx           +-> NodesStatsRequest              or JSON)
                        |   (data:true nodes)
                        +-> NodesStatsResponse
                        |   (NodeStats[])
                        +-> buildTable()
                        |   (Table object)
                        +-> RestTable.buildResponse()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| RestFielddataAction.java | `server/src/main/java/org/opensearch/rest/action/cat/RestFielddataAction.java` | ソース | 帳票メインクラス（124行） |
| AbstractCatAction.java | `server/src/main/java/org/opensearch/rest/action/cat/AbstractCatAction.java` | ソース | Cat API基底クラス |
| RestTable.java | `server/src/main/java/org/opensearch/rest/action/cat/RestTable.java` | ソース | テーブルレスポンス構築 |
| Table.java | `server/src/main/java/org/opensearch/common/Table.java` | ソース | テーブルデータ構造 |
| NodesStatsRequest.java | `server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodesStatsRequest.java` | ソース | ノード統計リクエスト |
| NodesStatsResponse.java | `server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodesStatsResponse.java` | ソース | ノード統計レスポンス |
| NodeStats.java | `server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodeStats.java` | ソース | 個別ノード統計 |
| ByteSizeValue.java | `server/src/main/java/org/opensearch/core/common/unit/ByteSizeValue.java` | ソース | バイトサイズ値表現 |
| ActionModule.java | `server/src/main/java/org/opensearch/action/ActionModule.java` | ソース | アクション登録（RestFielddataActionのルーティング登録） |
