# 通知設計書 34-SparkListenerExecutorUnblacklisted

## 概要

本ドキュメントは、Apache SparkにおけるSparkListenerExecutorUnblacklistedイベント通知の設計について記述する。本イベントはExecutorがブラックリスト（除外リスト）から解除された際に発火する非推奨イベントであり、Spark 3.1.0以降はSparkListenerExecutorUnexcludedに置き換えられている。

### 本通知の処理概要

SparkListenerExecutorUnblacklistedは、以前除外（ブラックリスト）されたExecutorが一定時間経過後に再有効化された際に発火するイベント通知である。

**業務上の目的・背景**：除外されたExecutorを永久に除外し続けると、クラスタリソースの浪費につながる。一時的な障害が解消された可能性があるため、設定されたタイムアウト期間（デフォルト1時間）経過後にExecutorを再有効化し、再びタスクの割り当て対象とする。本通知はその再有効化をリスナーに通知する。

**通知の送信タイミング**：HealthTracker.applyExcludeOnFailureTimeoutメソッド内で、除外されたExecutorのexpiryTimeが現在時刻を過ぎた場合に発火する。

**通知の受信者**：LiveListenerBusに登録された全てのSparkListenerInterface実装。

**通知内容の概要**：再有効化されたExecutorのIDおよび再有効化時刻が含まれる。

**期待されるアクション**：Spark UIでExecutorの除外状態が解除されたことが反映される。運用者はExecutorが再びアクティブになったことを確認し、引き続きモニタリングを行う。

## 通知種別

アプリ内通知（Sparkイベントバスによるインプロセス非同期イベント配信）

## 送信仕様

### 基本情報

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

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

LiveListenerBusに登録された全てのSparkListenerInterface実装に対してブロードキャスト配信される。SparkListenerBus.doPostEventのパターンマッチにより`onExecutorUnblacklisted`メソッドが呼び出される。

## 通知テンプレート

### イベントオブジェクト

| 項目 | 内容 |
|-----|------|
| イベントクラス | `SparkListenerExecutorUnblacklisted` |
| パッケージ | `org.apache.spark.scheduler` |
| 親クラス | `SparkListenerEvent` |
| シリアライズ形式 | JSON（JsonProtocol経由） |

### イベントデータ構造

```scala
@deprecated("use SparkListenerExecutorUnexcluded instead", "3.1.0")
case class SparkListenerExecutorUnblacklisted(time: Long, executorId: String)
  extends SparkListenerEvent
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| time | 再有効化時刻（ミリ秒） | `clock.getTimeMillis()` (now) | Yes |
| executorId | 再有効化されたExecutor ID | executorIdToExcludedStatus.keys | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 内部処理 | タイムアウト期限到達 | 除外Executorのexpiry時刻 < 現在時刻 | HealthTracker.applyExcludeOnFailureTimeout内で発火 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| タイムアウト未到達 | expiryTimeがまだ現在時刻を過ぎていない場合はイベントが発行されない |
| excludeOnFailure無効 | HealthTrackerが生成されない場合はイベント自体が発生しない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[applyExcludeOnFailureTimeout呼び出し] --> B{現在時刻 > nextExpiryTime?}
    B -->|Yes| C[期限切れExecutorをフィルタ]
    B -->|No| Z[処理終了]
    C --> D{期限切れExecutorあり?}
    D -->|Yes| E[executorIdToExcludedStatusから除去]
    D -->|No| Z
    E --> F[SparkListenerExecutorUnblacklisted発行]
    E --> G[SparkListenerExecutorUnexcluded発行]
    F --> H[LiveListenerBusでリスナーに配信]
    G --> H
    H --> I[nodeToExcludedExecsから該当Executor除去]
    I --> Z
```

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

該当なし（インメモリのデータ構造を使用）

### インメモリデータ構造の更新

| データ構造 | 操作 | 説明 |
|-----------|------|------|
| executorIdToExcludedStatus | remove | 期限切れExecutorを除外リストから除去 |
| nodeToExcludedExecs | remove | ノード上の除外Executor集合から除去 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| イベント配信失敗 | ListenerBusキュー溢れ | LiveListenerBusのドロップポリシーに従う |

### リトライ仕様

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

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし

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

- 本イベントはクラスタ内部の情報のみを含み、個人情報は含まない
- LiveListenerBusはJVMプロセス内での配信のみ

## 備考

- 本イベントは`@deprecated`であり、SparkListenerExecutorUnexcludedが推奨される
- タイムアウト値はEXCLUDE_ON_FAILURE_TIMEOUT_MILLIS（デフォルト1時間）で制御される
- applyExcludeOnFailureTimeoutは定期的に呼び出されるが、nextExpiryTimeによる最適化で不要な走査を回避する

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | SparkListener.scala | `core/src/main/scala/org/apache/spark/scheduler/SparkListener.scala` | 184-187行目: SparkListenerExecutorUnblacklistedのcase class定義。timeとexecutorIdのみの簡潔な構造 |

**読解のコツ**: 除外イベント（Blacklisted）はtaskFailuresフィールドを持つが、解除イベント（Unblacklisted）はExecutor IDのみで失敗数は不要。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | HealthTracker.scala | `core/src/main/scala/org/apache/spark/scheduler/HealthTracker.scala` | 105-143行目: applyExcludeOnFailureTimeoutメソッド。タイムアウト期限到達した除外をクリーンアップ |

**主要処理フロー**:
1. **109行目**: `now > nextExpiryTime`で高速チェック
2. **111行目**: `executorIdToExcludedStatus.filter(_._2.expiryTime < now)`で期限切れExecutorを抽出
3. **120行目**: `listenerBus.post(SparkListenerExecutorUnblacklisted(now, exec))`でイベント発行
4. **121行目**: 同時にSparkListenerExecutorUnexcludedも発行

#### Step 3: イベント配信メカニズムを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | SparkListenerBus.scala | `core/src/main/scala/org/apache/spark/scheduler/SparkListenerBus.scala` | 72-73行目: doPostEvent内でonExecutorUnblacklistedにルーティング |

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

```
TaskSchedulerImpl (定期的なスケジューリングサイクル)
    |
    +-- HealthTracker.applyExcludeOnFailureTimeout()
            |
            +-- executorIdToExcludedStatus.filter(期限切れ)
            +-- executorIdToExcludedStatus.remove(exec)
            +-- listenerBus.post(SparkListenerExecutorUnblacklisted)
            +-- listenerBus.post(SparkListenerExecutorUnexcluded)
            +-- nodeToExcludedExecs更新
            +-- updateNextExpiryTime()
```

### データフロー図

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

現在時刻(now)             HealthTracker                    SparkListenerExecutorUnblacklisted
除外リスト           ---> .applyExcludeOnFailureTimeout()   イベント
(executorId,              タイムアウト判定・除去               |
 expiryTime)                                                v
                                                     LiveListenerBus
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| SparkListener.scala | `core/src/main/scala/org/apache/spark/scheduler/SparkListener.scala` | ソース | イベントクラス定義 |
| SparkListenerBus.scala | `core/src/main/scala/org/apache/spark/scheduler/SparkListenerBus.scala` | ソース | イベントディスパッチ |
| HealthTracker.scala | `core/src/main/scala/org/apache/spark/scheduler/HealthTracker.scala` | ソース | タイムアウト処理・イベント発行元 |
| AppStatusListener.scala | `core/src/main/scala/org/apache/spark/status/AppStatusListener.scala` | ソース | UI向けイベント受信 |
| EventLoggingListener.scala | `core/src/main/scala/org/apache/spark/scheduler/EventLoggingListener.scala` | ソース | イベントログへの永続化 |
