# 機能設計書 104-node:async_hooks

## 概要

本ドキュメントは、BunにおけるNode.js互換の`node:async_hooks`モジュールの機能設計を記述したものである。このモジュールは、非同期リソースのライフサイクル追跡機能を提供する。

### 本機能の処理概要

`node:async_hooks`モジュールは、非同期操作のコンテキスト管理を提供する。BunではNode.js完全互換ではなく、`AsyncLocalStorage`と`AsyncResource`を中心に実装している。パフォーマンスへの影響を最小限に抑えるため、`createHook`などの低レベルフック機能は限定的なサポートとなっている。

**業務上の目的・背景**：非同期プログラミングにおいて、リクエストコンテキスト（ユーザー情報、トレースID等）を非同期処理間で引き継ぐことは重要な課題である。`AsyncLocalStorage`は、スレッドローカルストレージのような機能を非同期コンテキストで実現し、リクエストトレーシングやログ管理などに活用される。

**機能の利用シーン**：
- HTTPリクエストごとのコンテキスト管理（ユーザー情報、リクエストID）
- 分散トレーシング（OpenTelemetry等）
- ロギングでのコンテキスト伝播
- トランザクション管理

**主要な処理内容**：
1. `AsyncLocalStorage`: 非同期処理間でのコンテキスト伝播
2. `AsyncResource`: 非同期リソースのラッパー
3. `createHook`: 非同期操作のライフサイクルフック（限定サポート）
4. コンテキストのスナップショットと復元

**関連システム・外部連携**：
- JavaScriptCore（JSC）: 内部フィールドタプルでコンテキストを管理
- Promiseチェーン: コンテキストの自動伝播

**権限による制御**：特になし

## 関連画面

本機能はバックエンドモジュールであり、直接的な関連画面は存在しない。

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | - |

## 機能種別

コンテキスト管理 / 非同期フック

## 入力仕様

### 入力パラメータ

#### `AsyncLocalStorage.run(store, callback, ...args)`

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| store | any | Yes | 保存する値 | - |
| callback | function | Yes | 実行するコールバック | 関数であること |
| args | any[] | No | コールバックへの引数 | - |

#### `AsyncLocalStorage.enterWith(store)`

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| store | any | Yes | 保存する値 | - |

#### `AsyncResource`コンストラクタ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| type | string | Yes | リソースタイプ名 | 文字列であること |
| options | object \| number | No | オプション | - |
| options.triggerAsyncId | number | No | トリガー非同期ID | 安全な整数、>=-1 |

#### `createHook(hook)`

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| hook | object | Yes | フックオブジェクト | オブジェクトであること |
| hook.init | function | No | 初期化フック | 関数であること |
| hook.before | function | No | 実行前フック | 関数であること |
| hook.after | function | No | 実行後フック | 関数であること |
| hook.destroy | function | No | 破棄フック | 関数であること |
| hook.promiseResolve | function | No | Promise解決フック | 関数であること |

### 入力データソース

- 関数呼び出しの引数
- JSCの`$asyncContext`内部フィールド

## 出力仕様

### 出力データ

#### `AsyncLocalStorage.getStore()`の戻り値

| 項目名 | 型 | 説明 |
|--------|-----|------|
| store | any \| undefined | 現在のコンテキストの保存値、なければundefined |

#### `AsyncLocalStorage.run()`の戻り値

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | any | コールバックの戻り値 |

#### `AsyncResource.runInAsyncScope()`の戻り値

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | any | コールバックの戻り値 |

### 出力先

- 関数の戻り値
- `getStore()`によるコンテキスト値の取得

## 処理フロー

### 処理シーケンス（AsyncLocalStorage.run）

```
1. 現在のコンテキスト取得
   └─ get()で$asyncContextから取得
2. 新しいコンテキスト設定
   └─ [this, store_value]をコンテキスト配列に追加
3. コールバック実行
   └─ try内でコールバックを実行
4. コンテキスト復元（finally）
   └─ 前のコンテキストに復元
5. 結果を返却
```

### フローチャート

```mermaid
flowchart TD
    A[run呼び出し] --> B[現在のコンテキスト取得]
    B --> C{コンテキスト存在?}
    C -->|No| D[新規コンテキスト作成]
    C -->|Yes| E{既存エントリ?}
    E -->|Yes| F[値を更新]
    E -->|No| G[エントリを追加]
    D --> H[set でコンテキスト設定]
    F --> H
    G --> H
    H --> I[callback実行]
    I --> J{例外?}
    J -->|Yes| K[例外をスロー]
    J -->|No| L[結果を返却]
    K --> M[finallyでコンテキスト復元]
    L --> M
    M --> N[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-401 | コンテキスト配列形式 | [key, value, key, value, ...]のペア配列 | 常時 |
| BR-402 | 空コンテキスト | コンテキストが空の場合はundefined | コンテキストなし時 |
| BR-403 | disable後のgetStore | disableされたALSはundefinedを返す | disable()後 |
| BR-404 | enterWith/runによる再有効化 | disable後もenterWith/runで再有効化 | enterWith/run呼び出し時 |
| BR-405 | createHook警告 | 未実装のため警告を出力 | createHook使用時 |

### 計算ロジック

**コンテキスト検索**：
- コンテキスト配列を2要素ずつスキャン
- `context[i] === this`でAsyncLocalStorageインスタンスを検索
- 見つかれば`context[i+1]`が保存値

**コンテキストスナップショット**：
- `AsyncLocalStorage.snapshot()`で現在のコンテキストをキャプチャ
- 返されたクロージャ内でコンテキストを復元して関数実行

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

本機能はデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ERR_INVALID_ARG_TYPE | TypeError | fnが関数でない | 関数を指定 |
| ERR_INVALID_ASYNC_ID | RangeError | triggerAsyncIdが不正 | 有効なIDを指定 |
| ERR_ASYNC_TYPE | TypeError | typeが空文字列（createHook有効時） | 非空文字列を指定 |
| ERR_ASYNC_CALLBACK | TypeError | hookのコールバックが関数でない | 関数を指定 |

### リトライ仕様

本機能はリトライを行わない。

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

本機能はトランザクションを使用しない。

## パフォーマンス要件

- コンテキストが空の場合はオーバーヘッドなし
- `withAsyncContextIfNeeded()`でコンテキストが空ならラッパーを作成しない
- コンテキスト配列は不変（イミュータブル）として管理し、変更時はコピー

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

- コンテキストには任意の値を格納可能であり、機密情報を含む場合は適切に管理
- `getStore()`で他のコードからコンテキスト値にアクセス可能

## 備考

- `createHook`、`executionAsyncId`、`triggerAsyncId`はNode.js互換性のためにスタブ実装
- `executionAsyncResource`は常に`process.stdin`を返す（互換性スタブ）
- `asyncWrapProviders`定数はNode.js互換性のために提供

---

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

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

### 推奨読解順序

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

まず、非同期コンテキストの管理方法を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | async_hooks.ts | `src/js/node/async_hooks.ts` | コンテキスト配列の形式と管理 |

**読解のコツ**:
- **1-24行目**: モジュールの設計思想とコンテキスト管理の仕組みを説明
- **21-24行目**: AsyncContextDataは`[key, value, key, value]`形式の不変配列
- **30-52行目**: `assertValidAsyncContextArray`でコンテキスト配列の検証（デバッグ用）
- **65-74行目**: `get`/`set`関数で$asyncContextの内部フィールドにアクセス

#### Step 2: AsyncLocalStorageクラスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | async_hooks.ts | `src/js/node/async_hooks.ts` | AsyncLocalStorageの実装 |

**主要処理フロー**:
1. **76-91行目**: コンストラクタでasyncHooksを有効化
2. **93-109行目**: `bind`/`snapshot`で静的メソッドによるコンテキストスナップショット
3. **111-134行目**: `enterWith`で現在のコンテキストにエントリを追加
4. **142-205行目**: `run`でコンテキストを設定してコールバック実行、finally復元
5. **207-223行目**: `disable`でエントリを削除
6. **225-235行目**: `getStore`でコンテキストから値を取得

#### Step 3: AsyncResourceクラスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | async_hooks.ts | `src/js/node/async_hooks.ts` | AsyncResourceの実装 |

**主要処理フロー**:
- **256-279行目**: コンストラクタでコンテキストをスナップショット
- **301-309行目**: `runInAsyncScope`でスナップショットしたコンテキストで関数実行
- **311-319行目**: `bind`でコンテキストをバインドした関数を返す

#### Step 4: C++ネイティブ実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | AsyncContextFrame.cpp | `src/bun.js/bindings/AsyncContextFrame.cpp` | C++でのコンテキストフレーム管理 |

**主要処理フロー**:
- **15-30行目**: `AsyncContextFrame::create`でコンテキストフレームを作成
- **37-53行目**: `withAsyncContextIfNeeded`でコンテキストがあればラッパーを作成
- **120-131行目**: `call`でコンテキストを復元しながら関数を呼び出し

#### Step 5: スタブ実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | async_hooks.ts | `src/js/node/async_hooks.ts` | 未実装機能のスタブ |

**読解のコツ**:
- **324-353行目**: `createWarning`で警告を1回だけ出力するラッパー
- **362-389行目**: `createHook`のスタブ実装（警告を出力）
- **391-409行目**: `executionAsyncId`、`executionAsyncResource`のスタブ
- **411-470行目**: `asyncWrapProviders`定数（Node.js互換性）

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

```
JavaScript
    │
    ├─ AsyncLocalStorage.run(store, callback)
    │      ├─ get() → $asyncContext内部フィールド読み取り
    │      ├─ set() → $asyncContext内部フィールド書き込み
    │      └─ callback() → ユーザーコード実行
    │             └─ getStore() → get()で現在値取得
    │
    ├─ AsyncLocalStorage.enterWith(store)
    │      └─ cleanupLater() [C++]
    │             └─ 遅延クリーンアップをスケジュール
    │
    ├─ AsyncResource.runInAsyncScope(fn, thisArg)
    │      ├─ get() → 現在のコンテキスト保存
    │      ├─ set(#snapshot) → スナップショットを復元
    │      └─ fn.$apply() → 関数実行
    │
    └─ Native callback (Zig/C++)
           └─ AsyncContextFrame::withAsyncContextIfNeeded()
                  └─ AsyncContextFrame::call()
                         └─ profiledCall() with context restoration
```

### データフロー図

```
[コンテキスト設定]              [処理]                      [コンテキスト取得]

als.run(value, cb) ──────────▶ set([als, value]) ─────────▶ $asyncContext
                                    │
                                    ▼
                              callback() ─────────────────▶ als.getStore()
                                    │                            │
                                    │                            ▼
                                    │                      get() → context
                                    │                            │
                                    │                            ▼
                                    │                      context検索 → value
                                    │
                              finally: restore ───────────▶ $asyncContext復元
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| async_hooks.ts | `src/js/node/async_hooks.ts` | ソース | async_hooksモジュールの実装 |
| AsyncContextFrame.cpp | `src/bun.js/bindings/AsyncContextFrame.cpp` | ソース | C++でのコンテキストフレーム管理 |
| AsyncContextFrame.h | `src/bun.js/bindings/AsyncContextFrame.h` | ヘッダー | AsyncContextFrameクラスの宣言 |
| NodeAsyncHooks.cpp | `src/bun.js/bindings/NodeAsyncHooks.cpp` | ソース | ネイティブ関数（setAsyncHooksEnabled等） |
| validators.ts | `src/js/internal/validators.ts` | ソース | 入力検証関数群 |
