# 画面設計書 37-TaskManagerプロファイラ

## 概要

本ドキュメントは、Apache Flink Web DashboardのTaskManagerプロファイラ画面の設計仕様を定義する。この画面はTaskManagerのCPUプロファイリング機能を提供し、async-profilerを使用したパフォーマンス分析を支援する。

### 本画面の処理概要

**業務上の目的・背景**：分散処理システムにおいて、CPU使用率の高いホットスポットやパフォーマンスボトルネックを特定することは、ジョブの最適化に不可欠である。本画面は、async-profilerを利用したCPUプロファイリングをWeb UI上で実行できるようにし、フレームグラフ（FlameGraph）形式の結果を提供する。これにより、従来サーバーに直接アクセスして行っていたプロファイリング作業をWeb経由で簡便に実施でき、パフォーマンス問題の迅速な診断を支援する。複数のプロファイリングモード（CPU、Lock、Wall-Clock、Allocation、ITIMER）をサポートし、様々な観点からの分析が可能。

**画面へのアクセス方法**：TaskManager詳細画面のナビゲーションタブから「Profiler」を選択してアクセスする。URLは `/task-manager/:taskManagerId/profiler` である。

**主要な操作・処理内容**：
1. プロファイリング設定（Duration、Mode）の入力
2. プロファイリングインスタンスの作成・実行
3. プロファイリング履歴の一覧表示
4. プロファイリング結果（FlameGraph）のダウンロード
5. StatusServiceのrefresh$による定期的なステータス更新

**画面遷移**：
- 遷移元：TaskManager詳細画面のナビゲーション
- 遷移先：なし

**権限による表示制御**：プロファイリング機能は設定（rest.profiling.enabled: true）が必要。無効の場合は警告メッセージを表示し、実行ボタンは無効化される。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 32 | REST API | 主機能 | プロファイリング情報の取得（/taskmanagers/{taskmanagerid}/profiler API） |
| 57 | メトリクスコア | 補助機能 | プロファイリングインスタンスの作成・結果取得 |

## 画面種別

操作画面

## URL/ルーティング

`/task-manager/:taskManagerId/profiler`

## 入出力項目

| 項目名 | 入出力 | 型 | 必須 | 説明 |
|--------|--------|------|------|------|
| taskManagerId | パスパラメータ（入力） | string | 必須 | TaskManagerの一意識別子 |
| duration | 入力フィールド | number | 必須 | プロファイリング時間（秒）、デフォルト30秒 |
| selectMode | 入力フィールド | string | 必須 | プロファイリングモード、デフォルトITIMER |

## 表示項目

### プロファイリング設定フォーム

| 項目名 | データ型 | 説明 |
|--------|----------|------|
| Profiling Duration | number | プロファイリング時間（秒単位、min=1、step=30） |
| Profiling Mode | select | プロファイリングモード（CPU/Lock/Wall-Clock/Allocation/ITIMER） |
| Create Profiling Instance | button | プロファイリング実行ボタン |
| Info Icon | tooltip | async-profiler wiki へのリンク |

### プロファイリングモード詳細

| モード | 説明 |
|--------|------|
| CPU | Java methods、native calls、JVM code、kernel functionsを含むスタックトレースを収集 |
| Lock | ロック/モニターのクラスと、ロック取得にかかった時間（ナノ秒）を収集 |
| Wall-Clock | スレッドステータスに関係なく、一定間隔ですべてのスレッドをサンプリング |
| Allocation | 割り当てられたオブジェクトのクラスとヒープ圧力を収集 |
| ITIMER | CPUモードに類似、perf_eventsなしで動作（カーネルスタックトレースなし） |

### 警告メッセージ（機能無効時）

| 項目名 | データ型 | 説明 |
|--------|----------|------|
| Alert | nz-alert | "You need to set the config `rest.profiling.enabled: true` to enable this experimental profiler feature." |

### プロファイリング履歴テーブル

| 項目名 | データ型 | 説明 |
|--------|----------|------|
| Index | number | プロファイリングインスタンスのインデックス |
| Trigger Time | Date | プロファイリング開始時刻 |
| Finished Time | Date | プロファイリング完了時刻 |
| Profiling Duration | number | プロファイリング時間（秒） |
| Mode | string | プロファイリングモード |
| Status | string | ステータス（RUNNING/FINISHED/FAILED等） |
| Link | string | 結果ファイルへのダウンロードリンク |
| Message | string | メッセージ（エラー等） |

## イベント仕様

### 1-Create Profiling Instanceボタンクリック

- **イベント種別**：クリック
- **処理内容**：
  1. 実行中のプロファイリングがある場合は警告メッセージを表示して終了
  2. createProfilingInstance()でPOST APIを呼び出し
  3. 成功時、結果をprofilingListの先頭に追加
- **メソッド**：createProfilingInstance()
- **API**：POST /taskmanagers/{taskmanagerid}/profiler

### 2-結果ファイルリンククリック

- **イベント種別**：クリック
- **処理内容**：プロファイリング結果をダウンロード
- **メソッド**：downloadProfilingResult(filePath)
- **API**：GET /taskmanagers/{taskmanagerid}/profiler/{filePath}

### 3-自動リフレッシュ

- **イベント種別**：StatusService.refresh$のサブスクリプション
- **処理内容**：プロファイリング履歴を定期的に再取得
- **更新タイミング**：StatusServiceのリフレッシュ間隔に従う

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面表示・自動更新 | - | SELECT | REST API経由でプロファイリング履歴を取得 |
| プロファイリング実行 | - | INSERT | REST API経由でプロファイリングインスタンスを作成 |

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

本画面はREST API経由での操作のみを行い、データベースの直接更新は行わない。

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|------------|------|--------------|----------|
| - | Warning | "Please wait for last profiling finished." | 実行中のプロファイリングがある状態で実行ボタンをクリックした場合 |
| - | Alert | "You need to set the config `rest.profiling.enabled: true` to enable this experimental profiler feature." | プロファイリング機能が無効の場合 |
| - | Tooltip | 各プロファイリングモードの詳細説明 | モード選択ドロップダウンのオプションホバー時 |

## 例外処理

| 例外種別 | 処理内容 |
|---------|---------|
| プロファイリング履歴取得エラー | isLoading=falseに設定、リフレッシュを停止 |
| プロファイリング作成エラー | isCreating=falseに設定 |

## 備考

- async-profilerを使用（外部リンク：https://github.com/async-profiler/async-profiler/wiki）
- プロファイリング機能は実験的機能（experimental）として提供
- 設定 `rest.profiling.enabled: true` が必要
- isEnabled フラグでボタンの有効/無効を制御
- ChangeDetectionStrategy.OnPushによる最適化が適用

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | job-profiler.ts | `flink-runtime-web/web-dashboard/src/app/interfaces/job-profiler.ts` | ProfilingDetail、ProfilingListインターフェースを確認 |

**読解のコツ**: ProfilingDetailにはtriggerTime、finishedTime、duration、mode、status、outputFile、messageが含まれる。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | task-manager-profiler.component.ts | `flink-runtime-web/web-dashboard/src/app/pages/task-manager/profiler/task-manager-profiler.component.ts` | ngOnInit()、createProfilingInstance()、downloadProfilingResult()のフロー |
| 2-2 | task-manager-profiler.component.html | `flink-runtime-web/web-dashboard/src/app/pages/task-manager/profiler/task-manager-profiler.component.html` | フォーム構造、テーブル構造、ツールチップの内容 |

**主要処理フロー**:
1. **行99-124**: ngOnInit()でloadProfilingList()を呼び出し、履歴を取得
2. **行79-97**: createProfilingInstance()でプロファイリングを作成
3. **行127-145**: downloadProfilingResult()で結果をダウンロード

#### Step 3: サービス層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | task-manager.service.ts | `flink-runtime-web/web-dashboard/src/app/services/task-manager.service.ts` | loadProfilingList()（124-126行目）、createProfilingInstance()（128-134行目）、loadProfilingResult()（136-148行目） |

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

```
TaskManagerProfilerComponent
    │
    ├─ ngOnInit()
    │      ├─ StatusService.refresh$
    │      └─ TaskManagerService.loadProfilingList(taskManagerId)
    │             └─ HttpClient.get('/taskmanagers/{id}/profiler')
    │
    ├─ createProfilingInstance()
    │      ├─ 実行中チェック（profilingList[0].status === 'RUNNING'）
    │      └─ TaskManagerService.createProfilingInstance(taskManagerId, mode, duration)
    │             └─ HttpClient.post('/taskmanagers/{id}/profiler', {mode, duration})
    │
    └─ downloadProfilingResult(filePath)
           └─ TaskManagerService.loadProfilingResult(taskManagerId, filePath)
                  └─ HttpClient.get('/taskmanagers/{id}/profiler/{filePath}')
```

### データフロー図

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

duration/mode    createProfilingInstance()   profilingList
フォーム入力  →   POST API呼び出し        →  テーブル更新
                          │
                          ▼
                    /taskmanagers/{id}/
                    profiler API
                          │
                          ▼
                    FlameGraph結果 → ダウンロード
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| task-manager-profiler.component.ts | `flink-runtime-web/web-dashboard/src/app/pages/task-manager/profiler/task-manager-profiler.component.ts` | ソース | メインコンポーネント |
| task-manager-profiler.component.html | `flink-runtime-web/web-dashboard/src/app/pages/task-manager/profiler/task-manager-profiler.component.html` | テンプレート | フォーム・テーブルHTML |
| task-manager-profiler.component.less | `flink-runtime-web/web-dashboard/src/app/pages/task-manager/profiler/task-manager-profiler.component.less` | スタイル | スタイル定義 |
| task-manager.service.ts | `flink-runtime-web/web-dashboard/src/app/services/task-manager.service.ts` | ソース | API通信サービス |
| job-profiler.ts | `flink-runtime-web/web-dashboard/src/app/interfaces/job-profiler.ts` | ソース | ProfilingDetail/ProfilingList型定義 |
| humanize-watermark.pipe.ts | `flink-runtime-web/web-dashboard/src/app/components/humanize-watermark.pipe.ts` | ソース | 日時フォーマット変換 |
