# 通知設計書 9-SparkListenerEnvironmentUpdate

## 概要

本ドキュメントは、Apache Sparkのコアイベント通知である`SparkListenerEnvironmentUpdate`の設計について記述する。このイベントは、環境プロパティが更新された際に発火するイベント通知である。

### 本通知の処理概要

SparkListenerEnvironmentUpdateは、Sparkアプリケーションの実行環境に関する詳細情報（JVM情報、Spark設定、システムプロパティ、クラスパスなど）をリスナーに通知するイベントである。

**業務上の目的・背景**：Sparkアプリケーションのデバッグ・トラブルシューティングにおいて、実行環境の情報は不可欠である。Spark UIの「Environment」タブにJVM情報、Spark設定、Hadoop設定、システムプロパティ、クラスパス情報を表示するために利用される。また、イベントログに記録されることで、履歴サーバーからも環境情報を参照できる。セキュリティ上の配慮として、パスワード等の秘密情報は秘匿化（redact）される仕組みが組み込まれている。

**通知の送信タイミング**：SparkContext内の`postEnvironmentUpdate`メソッドで、TaskSchedulerの準備が完了した後に発火する（SparkContext.scala L2957-2968）。SparkContextの初期化プロセスの一部として一度だけ実行される。

**通知の受信者**：SparkListenerInterfaceを実装し、LiveListenerBusに登録されたすべてのリスナーが受信する。

**通知内容の概要**：environmentDetails（Map[String, Seq[(String, String)]]型）で、キーがカテゴリ名（「JVM Information」「Spark Properties」「Hadoop Properties」「System Properties」等）、値がキー・バリューペアのシーケンスとなるMap形式で環境情報を含む。

**期待されるアクション**：受信したリスナーは、環境情報をUIに表示する、イベントログに記録する（秘匿化後）。

## 通知種別

アプリ内通知（Sparkイベントバス経由の非同期インプロセス通知）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（LiveListenerBus経由） |
| 優先度 | 中 |
| リトライ | 無し |

### 送信先決定ロジック

LiveListenerBusに登録された全リスナーに対してブロードキャスト配信される。

## 通知テンプレート

### メール通知の場合

本イベントはメール通知ではなく、Sparkイベントバス経由のインプロセス通知である。

### 本文テンプレート

```
イベントクラス: SparkListenerEnvironmentUpdate
シリアライズ形式: JSON（Jackson, @JsonTypeInfo）

{
  "Event": "org.apache.spark.scheduler.SparkListenerEnvironmentUpdate",
  "JVM Information": { ... },
  "Spark Properties": { ... },
  "Hadoop Properties": { ... },
  "System Properties": { ... },
  "Metrics Properties": { ... },
  "Classpath Entries": { ... }
}
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| 該当なし | - | - | インプロセス通知のため添付ファイルなし |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| environmentDetails | 環境情報のMap | SparkEnv.environmentDetails() | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| SparkContext初期化 | SparkContext.postEnvironmentUpdate | TaskScheduler準備完了後 | SparkContext.scala L2957-2968 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| taskSchedulerがnull | taskSchedulerが初期化されていない場合はpostされない（L2958の条件チェック） |
| LiveListenerBus停止後 | イベントは配信されない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[SparkContext初期化] --> B[TaskScheduler準備完了]
    B --> C[postEnvironmentUpdate呼び出し]
    C --> D{taskScheduler != null?}
    D -->|No| E[何もしない]
    D -->|Yes| F[SparkEnv.environmentDetails取得]
    F --> G[SparkListenerEnvironmentUpdate生成]
    G --> H[listenerBus.post]
    H --> I{イベントログ出力時}
    I --> J[EventLoggingListener.redactEvent でプロパティ秘匿化]
    J --> K[秘匿化済みイベントをログ記録]
    H --> L[各リスナーのonEnvironmentUpdate呼び出し]
    L --> M[終了]
    K --> M
    E --> M
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| KVStore | AppStatusListenerが環境情報を格納 | UI表示用 |

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

#### KVStore

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| ApplicationEnvironmentInfoWrapper | 環境情報の表示 | アプリケーション単位で1レコード |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| KVStore | INSERT/UPDATE | AppStatusListenerが環境情報を格納 |
| イベントログファイル | APPEND | 秘匿化後のイベントをJSON記録 |

#### 送信ログテーブル

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| WRITE | ApplicationEnvironmentInfoWrapper | 環境情報一式 | AppStatusListenerによる書き込み |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| キューオーバーフロー | イベント生成速度超過 | イベントドロップ |
| リスナー例外 | onEnvironmentUpdate内で例外 | 例外キャッチ、ログ出力 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0 |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| キュー容量 | デフォルト10,000イベント |

### 配信時間帯

時間帯制限なし。通常、SparkContext初期化時に1回のみ発火。

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

- **秘匿化（Redaction）処理**: ListenerBus内の`redactEvent`メソッド（ListenerBus.scala L158-162）により、EventLoggingListenerへの配信時にパスワードやシークレット等の秘密情報が秘匿化される。`EventLoggingListener.redactEvent`がSparkListenerEnvironmentUpdateイベント専用の秘匿化処理を行う。
- environmentDetailsにはSpark設定、Hadoop設定、システムプロパティが含まれ、認証情報や接続文字列が含まれる可能性がある
- イベントログファイルには秘匿化後の情報が記録される
- KVStoreにはリスナーが受信した元の情報が格納される可能性があるため、Spark UIへのアクセス制御が必要

## 備考

- SparkListenerEnvironmentUpdateはenvironmentDetailsのみの1フィールドケースクラスである（SparkListener.scala L98-100）
- environmentDetailsの型は`Map[String, collection.Seq[(String, String)]]`である
- SparkEnv.environmentDetailsメソッド（SparkEnv.scala L505-507）が環境情報の収集を行い、引数としてconf, hadoopConfiguration, schedulingMode, addedJarPaths, addedFilePaths, addedArchivePaths, metricsPropertiesを受け取る
- このイベントは通常SparkContext初期化時に1回のみ発火される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | SparkListener.scala | `core/src/main/scala/org/apache/spark/scheduler/SparkListener.scala` | L98-100: `case class SparkListenerEnvironmentUpdate(environmentDetails: Map[String, collection.Seq[(String, String)]])`。environmentDetailsのMap構造を理解する |

**読解のコツ**: environmentDetailsのキーは「JVM Information」「Spark Properties」「Hadoop Properties」「System Properties」「Metrics Properties」「Classpath Entries」等のカテゴリ名。値は(キー, 値)のペアのシーケンス。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | SparkContext.scala | `core/src/main/scala/org/apache/spark/SparkContext.scala` | L2957-2968: postEnvironmentUpdateメソッド。taskSchedulerのnullチェック後、SparkEnv.environmentDetailsで環境情報を収集し、SparkListenerEnvironmentUpdateを生成してpost |
| 2-2 | SparkEnv.scala | `core/src/main/scala/org/apache/spark/SparkEnv.scala` | L505-507: environmentDetailsメソッド。環境情報の収集ロジック |

**主要処理フロー**:
1. **SparkContext.scala L2958**: `if (taskScheduler != null)` チェック
2. **L2959-2962**: schedulingMode、addedJarPaths、addedFilePaths、addedArchivePathsを取得
3. **L2963-2965**: `SparkEnv.environmentDetails(...)` で環境情報をMap形式で収集
4. **L2966**: `SparkListenerEnvironmentUpdate(environmentDetails)` を生成
5. **L2967**: `listenerBus.post(environmentUpdate)` でpost

#### Step 3: 秘匿化処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ListenerBus.scala | `core/src/main/scala/org/apache/spark/util/ListenerBus.scala` | L158-162: redactEventメソッド。SparkListenerEnvironmentUpdateに対して`EventLoggingListener.redactEvent`を呼び出す |

#### Step 4: イベントディスパッチを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | SparkListenerBus.scala | `core/src/main/scala/org/apache/spark/scheduler/SparkListenerBus.scala` | L46-47: SparkListenerEnvironmentUpdateのパターンマッチ → listener.onEnvironmentUpdate()呼び出し |

#### Step 5: リスナー側の処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | AppStatusListener.scala | `core/src/main/scala/org/apache/spark/status/AppStatusListener.scala` | L168: onEnvironmentUpdateメソッド。event.environmentDetailsからJVM情報を抽出しKVStoreに格納 |

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

```
SparkContext.postEnvironmentUpdate() [SparkContext.scala L2957]
    |
    +-- SparkEnv.environmentDetails() [SparkEnv.scala L505]
    |       (環境情報の収集)
    |
    +-- listenerBus.post(SparkListenerEnvironmentUpdate(environmentDetails))
            |
            +-- LiveListenerBus.post()
                    |
                    +-- postToQueues()
                            |
                            +-- AsyncEventQueue.post()
                                    |
                                    +-- [配信スレッド] doPostEvent()
                                    |       |
                                    |       +-- [イベントログキュー] redactEvent() [ListenerBus.scala L158]
                                    |       |       +-- EventLoggingListener.redactEvent()
                                    |       |
                                    |       +-- listener.onEnvironmentUpdate()
                                    |
                                    +-- AppStatusListener.onEnvironmentUpdate()
                                    +-- EventLoggingListener (秘匿化済みイベントをログ記録)
```

### データフロー図

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

SparkContext                LiveListenerBus                  各リスナー
(SparkConf,                (AsyncEventQueue x N)
 HadoopConfiguration,
 SchedulingMode,
 addedJars/Files/Archives,                                   AppStatusListener
 metricsProperties)                                          (KVStore -> Spark UI Environment)

SparkEnv.environmentDetails                                  EventLoggingListener
        |                                                    (秘匿化 -> イベントログ)
        v
environmentDetails -------> SparkListenerEnvironmentUpdate
(Map[String,                        |
 Seq[(String,String)]])             +--[eventLog queue]---> redactEvent()
                                    |                        -> 秘匿化済みJSON記録
                                    +--[他キュー]----------> そのまま配信
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| SparkListener.scala | `core/src/main/scala/org/apache/spark/scheduler/SparkListener.scala` | ソース | SparkListenerEnvironmentUpdateケースクラス定義（L98-100） |
| SparkContext.scala | `core/src/main/scala/org/apache/spark/SparkContext.scala` | ソース | postEnvironmentUpdateメソッド（L2957-2968） |
| SparkEnv.scala | `core/src/main/scala/org/apache/spark/SparkEnv.scala` | ソース | environmentDetailsメソッド（L505-507） |
| ListenerBus.scala | `core/src/main/scala/org/apache/spark/util/ListenerBus.scala` | ソース | redactEvent秘匿化処理（L158-162） |
| SparkListenerBus.scala | `core/src/main/scala/org/apache/spark/scheduler/SparkListenerBus.scala` | ソース | イベントディスパッチ（L46-47） |
| AppStatusListener.scala | `core/src/main/scala/org/apache/spark/status/AppStatusListener.scala` | ソース | onEnvironmentUpdate処理（L168） |
| EventLoggingListener.scala | `core/src/main/scala/org/apache/spark/scheduler/EventLoggingListener.scala` | ソース | イベントログ記録・秘匿化 |
