# 機能設計書 18-History Server

## 概要

本ドキュメントは、Apache SparkのHistory Serverについて、完了済みアプリケーションの実行履歴を提供するサーバーの設計仕様を記述した機能設計書である。

### 本機能の処理概要

完了済みSparkアプリケーションのイベントログを読み取り、SparkUIを再構築して表示するWebサーバーを提供する。アプリケーション完了後でも実行履歴を閲覧・分析できるようにする。

**業務上の目的・背景**：Sparkアプリケーションが完了するとSparkUIも停止するが、事後分析やパフォーマンスチューニングのために実行履歴へのアクセスが必要な場合がある。History Serverは永続化されたイベントログからSparkUIを再構築し、常時アクセス可能な履歴閲覧環境を提供する。特にYARN等のデプロイモードで有用。

**機能の利用シーン**：(1) 完了済みアプリケーションのパフォーマンス分析、(2) 障害調査のための履歴参照、(3) 複数アプリケーションの比較分析、(4) 運用監視のためのダッシュボード。

**主要な処理内容**：
1. ApplicationHistoryProviderによるイベントログの定期スキャンと解析
2. ApplicationCacheによるSparkUIインスタンスのLRUキャッシュ管理
3. loaderServletによるアプリケーション別UI動的ロード
4. REST API（/api/v1/）の提供
5. イベントログのZIPダウンロード機能
6. Kerberos認証環境でのセキュアなログアクセス

**関連システム・外部連携**：HDFS/ローカルファイルシステム（イベントログストレージ）、Kerberos KDC（認証）、Hadoopセキュリティ基盤

**権限による制御**：History Server用ACL設定（spark.history.ui.acls.enable）により閲覧権限を制御。アプリケーションごとのACLも参照可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 28 | History Server Application List（履歴一覧） | 主機能 | 完了済みアプリケーション一覧をテーブル形式で表示 |
| 29 | History Server Application Detail（履歴詳細） | 主機能 | 選択アプリケーションのSparkUIを再構築・表示 |

## 機能種別

Web UI / 履歴管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| spark.history.fs.logDirectory | String | No | イベントログディレクトリ | デフォルト: file:/tmp/spark-events |
| spark.history.ui.port | Int | No | UIポート番号 | デフォルト: 18080 |
| spark.history.retainedApplications | Int | No | キャッシュ保持アプリ数 | デフォルト: 50 |
| spark.history.ui.maxApplications | Int | No | 一覧表示最大数 | デフォルト: INT_MAX |
| spark.history.ui.title | String | No | UIタイトル | - |
| spark.history.provider | String | No | ApplicationHistoryProvider実装クラス | デフォルト: FsHistoryProvider |
| spark.history.kerberos.enabled | Boolean | No | Kerberos認証有効化 | デフォルト: false |
| spark.history.kerberos.principal | String | Kerberos有効時 | Kerberos Principal | - |
| spark.history.kerberos.keytab | String | Kerberos有効時 | Keytabファイルパス | - |

### 入力データソース

イベントログディレクトリ内のJSONイベントログファイル（EventLoggingListenerが出力したもの）。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| アプリケーション一覧 | HTML/JSON | 完了済みアプリケーションのリスト |
| 再構築SparkUI | HTML | アプリケーション別のSparkUI画面 |
| イベントログZIP | ZipOutputStream | イベントログのダウンロード |

### 出力先

HTTPレスポンスとしてWebブラウザ・APIクライアントに返却。

## 処理フロー

### 処理シーケンス

```
1. HistoryServer起動
   └─ main()でSparkConf読み込み、SecurityManager生成、Provider生成
2. initialize()
   └─ HistoryPage, LogPage, REST API, 静的リソースハンドラの登録
3. bind()
   └─ Jettyサーバー起動
4. Provider開始
   └─ ApplicationHistoryProvider.start()でログディレクトリの定期スキャン開始
5. アプリケーションアクセス
   └─ loaderServletでリクエストを受信、appCacheからSparkUIを取得・構築
6. キャッシュ管理
   └─ ApplicationCacheでLRU方式によるSparkUIのキャッシュと破棄
```

### フローチャート

```mermaid
flowchart TD
    A[HistoryServer.main] --> B[initSecurity Kerberos対応]
    B --> C[SecurityManager生成]
    C --> D[ApplicationHistoryProvider生成]
    D --> E[HistoryServer生成]
    E --> F[initialize]
    F --> G[HistoryPage登録]
    F --> H[REST API登録]
    F --> I[loaderServlet登録 /history/*]
    I --> J[bind]
    J --> K[Provider.start]
    K --> L{アプリアクセス}
    L --> M[loaderServlet.doGet]
    M --> N[appId, attemptId解析]
    N --> O[appCache.withSparkUI]
    O --> P{キャッシュにあり?}
    P -->|Yes| Q[キャッシュからSparkUI取得]
    P -->|No| R[Provider.getAppUI]
    R --> S[SparkUI再構築]
    S --> T[キャッシュに格納]
    Q --> U[リダイレクト]
    T --> U
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-18-01 | UIパスプレフィックス | アプリケーション別UIは"/history/{appId}/{attemptId}/"パスで提供 | アプリアクセス時 |
| BR-18-02 | LRUキャッシュ | retainedApplications数を超えた場合、最古のキャッシュエントリを破棄 | キャッシュ管理 |
| BR-18-03 | 複数Attempt対応 | attemptIdが省略された場合、最新のattemptにリダイレクト | アプリアクセス時 |
| BR-18-04 | 認証無効化 | History Server用SecurityManager生成時にspark.authenticateをfalseに設定 | 起動時 |
| BR-18-05 | TRACEメソッド拒否 | SPARK-5983対応でHTTP TRACEメソッドを拒否 | 全HTTPアクセス |
| BR-18-06 | スレッドプール拡大 | 通常のSparkUI(デフォルト)と比較してpoolSize=1000に設定 | 起動時 |

### 計算ロジック

該当なし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | データベース操作なし（ファイルシステムベース） |

### テーブル別操作詳細

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 404 | NoSuchElementException | 存在しないappIdへのアクセス | 「Application not found」メッセージを表示 |
| - | NoSuchElementException | Kerberos設定のprincipal/keytab欠落 | 起動時に例外スロー |

### リトライ仕様

ApplicationHistoryProviderが定期的にログディレクトリをスキャンしてアプリケーション一覧を更新する。個別のUI構築失敗はキャッシュミスとして再試行可能。

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

該当なし

## パフォーマンス要件

スレッドプールサイズ1000により高並行アクセスに対応。ApplicationCacheのLRU管理によりメモリ使用量を制限。

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

- Kerberos認証環境でのKeytab管理
- History Server用ACL設定でアプリケーションごとの閲覧権限を制御可能
- HTTP TRACEメソッドの無効化（SPARK-5983）

## 備考

- Standaloneモードでは MasterWebUIが同等機能を提供するため、History Serverは主にYARN等の環境で使用
- ShutdownHookManagerによる正常停止処理が登録される
- EventLoggingListenerと同じディレクトリ構造を前提としている

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ApplicationHistoryProvider.scala | `core/src/main/scala/org/apache/spark/deploy/history/ApplicationHistoryProvider.scala` | Providerインターフェースの定義 |
| 1-2 | ApplicationCache.scala | `core/src/main/scala/org/apache/spark/deploy/history/ApplicationCache.scala` | LRUキャッシュの実装 |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | HistoryServer.scala | `core/src/main/scala/org/apache/spark/deploy/history/HistoryServer.scala` | メインクラス。main(), initialize(), loaderServletの流れ |

**主要処理フロー**:
1. **50-61行目**: コンストラクタでprovider, securityManager, portを受け取り初期化
2. **63-72行目**: title, retainedApplications, maxApplications, appCacheの設定
3. **77-132行目**: loaderServletのdoGet()でURLパース→アプリロード→リダイレクト
4. **151-164行目**: initialize()でページ・ハンドラ登録
5. **178-193行目**: attachSparkUI/detachSparkUIで動的にUI追加・削除
6. **299-326行目**: HistoryServer.main()でエントリーポイント処理

#### Step 3: 具体的なProvider実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | FsHistoryProvider.scala | `core/src/main/scala/org/apache/spark/deploy/history/FsHistoryProvider.scala` | ファイルシステムベースのプロバイダー実装 |

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

```
HistoryServer.main()
    │
    ├─ initSecurity() ─ Kerberos設定
    ├─ createSecurityManager()
    ├─ ApplicationHistoryProvider生成 (FsHistoryProvider)
    │
    └─ HistoryServer
         ├─ initialize()
         │    ├─ HistoryPage (アプリ一覧)
         │    ├─ LogPage
         │    ├─ ApiRootResource (/api/v1/)
         │    └─ loaderServlet (/history/*)
         │
         ├─ bind() → Jetty起動
         │
         ├─ provider.start() → ログディレクトリスキャン開始
         │
         └─ loaderServlet.doGet()
              ├─ appId/attemptId解析
              ├─ appCache.withSparkUI()
              │    └─ provider.getAppUI() → SparkUI再構築
              └─ attachSparkUI() → ハンドラ追加
```

### データフロー図

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

イベントログファイル  ───▶ FsHistoryProvider       ───▶ ApplicationInfo一覧
(HDFS/ローカル)            ├─ 定期スキャン
                           └─ ログ解析

ApplicationInfo    ───▶ HistoryPage             ───▶ アプリ一覧HTML
                        └─ テーブル表示

appId/attemptId    ───▶ ApplicationCache        ───▶ 再構築SparkUI
                        ├─ LRUキャッシュ管理
                        └─ getAppUI()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| HistoryServer.scala | `core/src/main/scala/org/apache/spark/deploy/history/HistoryServer.scala` | ソース | History Serverのメインクラス |
| ApplicationHistoryProvider.scala | `core/src/main/scala/org/apache/spark/deploy/history/ApplicationHistoryProvider.scala` | ソース | Provider抽象クラス |
| FsHistoryProvider.scala | `core/src/main/scala/org/apache/spark/deploy/history/FsHistoryProvider.scala` | ソース | ファイルシステムベースProvider |
| ApplicationCache.scala | `core/src/main/scala/org/apache/spark/deploy/history/ApplicationCache.scala` | ソース | SparkUIのLRUキャッシュ |
| HistoryPage.scala | `core/src/main/scala/org/apache/spark/deploy/history/HistoryPage.scala` | ソース | アプリケーション一覧ページ |
| HistoryServerArguments.scala | `core/src/main/scala/org/apache/spark/deploy/history/HistoryServerArguments.scala` | ソース | コマンドライン引数解析 |
