# 通知設計書 35-拡張機能ロードエラー

## 概要

本ドキュメントは、Julia のパッケージ拡張機能（Package Extensions）のロード中にエラーが発生した場合に出力されるエラー通知の設計を記述する。

### 本通知の処理概要

本通知は、パッケージ拡張機能（Julia 1.9 以降で導入された Extension 機構）のロード処理 `_require_prelocked` がエラーをスローした場合にエラーレベルのログを出力する処理である。

**業務上の目的・背景**：Julia のパッケージ拡張機能は、特定のトリガーパッケージがロードされた際に自動的にロードされる追加モジュールである。例えば、パッケージ A がパッケージ B への拡張機能を定義している場合、B がロードされると A の拡張が自動的にロードされる。この拡張機能のロードが失敗しても、メインのパッケージロード処理は続行させたい。本通知はそのエラーを記録し、後からリトライできるようにユーザーに情報を提供する。

**通知の送信タイミング**：`run_extension_callbacks(extid::ExtensionId)` 関数内で `_require_prelocked(extid.id)` がエラーをスローし、かつインクリメンタルプリコンパイルモードでない場合に送信される。具体的には `base/loading.jl` の1774行目で発火する。

**通知の受信者**：Julia プロセスの標準エラー出力 (stderr) を監視する開発者・運用者。ログレベルは `Error`。

**通知内容の概要**：`"Error during loading of extension {拡張名} of {親パッケージ名}, use Base.retry_load_extensions() to retry."` というメッセージと共に、例外スタックが出力される。

**期待されるアクション**：受信者は拡張機能のロードエラーの原因を調査する。また、`Base.retry_load_extensions()` を呼び出して拡張機能のロードをリトライできる。

## 通知種別

ログ（Error） -- Julia の標準ログシステム（`@error` マクロ）による stderr へのエラーレベル出力

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（`@error` マクロによる即座のログ出力） |
| 優先度 | 高 |
| リトライ | 有り（`Base.retry_load_extensions()` による手動リトライが可能） |

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

Julia の標準ログシステムにより、現在アクティブなログハンドラに送信される。

## 通知テンプレート

### メール通知の場合

該当なし（本通知はログ出力であり、メール送信は行わない）

### 本文テンプレート

```
Error during loading of extension {extension_name} of {parent_name}, use `Base.retry_load_extensions()` to retry.
  exception = {例外スタック}
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| extension_name | 拡張機能の名前 | `extid.id.name` | Yes |
| parent_name | 親パッケージの名前 | `extid.parentid.name` | Yes |
| exception | キャッチされた例外スタック | `current_exceptions()` | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| パッケージロード | 拡張機能の `_require_prelocked` がエラー発生 | インクリメンタルプリコンパイルモードでない場合 | `run_extension_callbacks(extid::ExtensionId)` 内の `catch` ブロック |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| `JLOptions().incremental != 0` | インクリメンタルプリコンパイル中はエラーが rethrow される（本通知は発行されない） |
| 拡張機能のロードが成功 | ロードが正常完了した場合は本通知は発生しない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A["トリガーパッケージがロードされた"] --> B["run_extension_callbacks(pkgid)"]
    B --> C["ExtensionId を取得"]
    C --> D["run_extension_callbacks(extid::ExtensionId)"]
    D --> E["loading_extension = true"]
    E --> F["_require_prelocked(extid.id)"]
    F --> G{"ロード成功か"}
    G -->|成功| H["@debug Extension loaded"]
    G -->|失敗 catch| I{"incremental != 0 か"}
    I -->|Yes| J["rethrow() -- プリコンパイルは即座に失敗"]
    I -->|No| K["@error 拡張機能ロードエラー"]
    K --> L["loading_extension = false (finally)"]
    H --> L
```

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

### 参照テーブル一覧

該当なし（データベースは使用しない）

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 拡張機能ロードエラー | `_require_prelocked` がエラーをスロー | @error 通知を発行、失敗した ExtensionId を EXT_DORMITORY_FAILED に追加 |
| プリコンパイル中のエラー | インクリメンタルプリコンパイルモードでロードエラー | rethrow() で即座に失敗 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 手動（`Base.retry_load_extensions()` による） |
| リトライ間隔 | ユーザー操作に依存 |
| リトライ対象エラー | すべての拡張機能ロードエラー |

## 配信設定

### レート制限

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

### 配信時間帯

制限なし

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

本通知は拡張機能名、親パッケージ名、および例外スタック情報を含む。これらはデバッグ用の情報であり、通常は機密情報を含まない。

## 備考

- インクリメンタルプリコンパイルモード（`JLOptions().incremental != 0`）では、本通知の代わりにエラーが rethrow される。これはプリコンパイル時にはフェイルファストが望ましいためである。
- 失敗した拡張機能は `EXT_DORMITORY_FAILED` 配列に追加され、`Base.retry_load_extensions()` で後からリトライできる。
- `loading_extension` グローバル変数は、拡張機能ロード中かどうかを示すフラグであり、Distributed パッケージなどから参照される。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | loading.jl | `base/loading.jl` | `ExtensionId` 構造体（1639-1644行目）と関連するグローバル変数 `EXT_PRIMED`, `EXT_DORMITORY`, `EXT_DORMITORY_FAILED`（1646-1648行目）を理解する |

**読解のコツ**: `ExtensionId` は拡張機能の ID (`id`)、親パッケージの ID (`parentid`)、トリガーパッケージの総数 (`n_total_triggers`)、残りのトリガー数 (`ntriggers`) を保持する。`ntriggers` が 0 になると拡張機能がロードされる。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | loading.jl | `base/loading.jl` | `run_extension_callbacks(extid::ExtensionId)` 関数（1759-1782行目）が本通知の直接の発火元 |

**主要処理フロー**:
1. **1760行目**: `assert_havelock(require_lock)` でロック確認
2. **1763行目**: `loading_extension = true` を設定
3. **1764行目**: `_require_prelocked(extid.id)` で拡張機能をロード
4. **1765行目**: 成功時に `@debug "Extension loaded"` を出力
5. **1769-1771行目**: インクリメンタルプリコンパイル時は `rethrow()`
6. **1773-1775行目**: 通常時は `@error` で本通知を発行

#### Step 3: 呼び出し元を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | loading.jl | `base/loading.jl` | `run_extension_callbacks(pkgid::PkgId)` 関数（1784行目以降）がトリガーパッケージロード時の処理 |

**主要処理フロー**:
- **1787行目**: `EXT_DORMITORY` から対象の `ExtensionId` を取得
- **1792行目**: `ntriggers` をデクリメント
- **1793行目**: `ntriggers == 0` の場合にロード対象リストに追加
- **1798行目**: トリガー数が少ない順にソートしてロード

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

```
require(modkey) [base/loading.jl]
    |
    +-- _require_prelocked(modkey)
            |
            +-- run_package_callbacks(modkey) [行1616]
                    |
                    +-- run_extension_callbacks(modkey::PkgId) [行1784]
                            |
                            +-- run_extension_callbacks(extid::ExtensionId) [行1759]
                                    |
                                    +-- _require_prelocked(extid.id) [行1764]
                                    |       (ここでエラー発生時)
                                    +-- @error (No.35 本通知) [行1774]
```

### データフロー図

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

トリガーパッケージ
(PkgId) ----------------> EXT_DORMITORY 検索
                               |
                               +-- ExtensionId 取得
                               |       |
                               |       +-- ntriggers デクリメント
                               |       |
                               |       +-- ntriggers == 0
                               |               |
                               |               +--> _require_prelocked(extid.id)
                               |                       |
                               |                       +-- 失敗 --> @error ログ --> stderr
                               |                       +-- 成功 --> @debug ログ
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| loading.jl | `base/loading.jl` | ソース | パッケージロードシステム、拡張機能の管理 |
| logging.jl | `base/logging.jl` | ソース | `@error` マクロの定義 |
