# 通知設計書 4-diagnostics_channel

## 概要

本ドキュメントは、Node.jsのdiagnostics_channelモジュールによるプログラム内部イベント通知機構の設計書である。

### 本通知の処理概要

diagnostics_channelは、Node.jsアプリケーション内部で発生するイベント（HTTPリクエスト、ネットワーク接続、コンソール出力等）を購読者に通知するためのPub/Sub（Publish/Subscribe）機構を提供する。これにより、アプリケーションの計装（instrumentation）やモニタリングを非侵入的に実現できる。

**業務上の目的・背景**：Node.jsアプリケーションの運用において、パフォーマンス監視、デバッグ、APM（Application Performance Monitoring）ツールとの連携が重要である。diagnostics_channelは、アプリケーションコードを変更することなく、内部イベントを外部ツールに通知する標準的な方法を提供する。これにより、本番環境での問題診断やパフォーマンス分析が容易になる。

**通知の送信タイミング**：チャンネルに対してpublish()メソッドが呼び出された時点で、登録されている全ての購読者（subscriber）に同期的に通知される。

**通知の受信者**：subscribe()メソッドでチャンネルに登録したコールバック関数。APMツール、ロギングライブラリ、カスタム監視コードなどが想定される。

**通知内容の概要**：チャンネル固有のデータオブジェクト。HTTPリクエストの場合はリクエスト/レスポンスオブジェクト、ネットワーク接続の場合はソケット情報など。

**期待されるアクション**：購読者は受信したデータを処理し、ログ出力、メトリクス収集、トレース生成などを行う。

## 通知種別

内部イベント通知（Pub/Sub機構）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期 |
| 優先度 | 高（パフォーマンスクリティカル） |
| リトライ | なし（同期実行のため） |

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

チャンネル名で識別される名前付きチャンネルに対してsubscribe()を呼び出した全ての購読者コールバックに送信される。購読者がいない場合は何も実行されない（hasSubscribersプロパティで事前チェック可能）。

## 通知テンプレート

### チャンネルAPI

| 項目 | 内容 |
|-----|------|
| チャンネル作成 | `dc.channel('channel-name')` |
| 購読登録 | `channel.subscribe(callback)` |
| 購読解除 | `channel.unsubscribe(callback)` |
| メッセージ送信 | `channel.publish(data)` |
| 購読者有無確認 | `channel.hasSubscribers` |

### データ構造例

```javascript
// HTTPクライアントリクエストの場合
{
  request: ClientRequest  // HTTPリクエストオブジェクト
}

// ネットワーク接続の場合
{
  socket: Socket  // ソケットオブジェクト
}
```

### 添付ファイル

該当なし（プログラム内部通知のため）

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| data | 通知データオブジェクト | publish()の引数 | Yes |
| name | チャンネル名 | channel.name | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| プログラム呼び出し | Channel.publish() | hasSubscribers == true | 購読者が存在する場合のみ実行 |
| プログラム呼び出し | Channel.runStores() | hasSubscribers == true | AsyncLocalStorageと連携した実行 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 購読者なし | hasSubscribersがfalseの場合、publish()は何も実行しない |
| チャンネル未アクティブ | 購読者もストアもない場合、Channelプロトタイプに戻る |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[イベント発生] --> B{hasSubscribers?}
    B -->|No| C[処理終了]
    B -->|Yes| D[購読者リスト取得]
    D --> E[各購読者にデータ送信]
    E --> F{購読者コールバック実行}
    F -->|エラー| G[process.nextTickでuncaughtException発火]
    F -->|成功| H[次の購読者へ]
    H --> E
    G --> H
```

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

### 参照テーブル一覧

本通知はデータベースを使用しない。

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| なし | - | メモリ内データ構造のみ使用 |

### 内部データ構造

| 構造名 | 用途 | 説明 |
|--------|------|------|
| channels (WeakRefMap) | チャンネル管理 | 名前をキーとしたチャンネルオブジェクトの弱参照マップ |
| _subscribers (Array) | 購読者リスト | チャンネルごとの購読者コールバック配列 |
| _stores (SafeMap) | ストア管理 | AsyncLocalStorageとの連携用マップ |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| なし | - | データベース更新なし |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 購読者コールバックエラー | 購読者内で例外発生 | process.nextTickでtriggerUncaughtException呼び出し、他の購読者は継続実行 |
| 無効な引数 | channel()に無効な型を渡した | ERR_INVALID_ARG_TYPE例外をスロー |
| 無効な購読者 | subscribe()に関数以外を渡した | validateFunction()で検証エラー |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし |
| リトライ間隔 | なし |
| リトライ対象エラー | なし（同期処理のため） |

## 配信設定

### レート制限

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

### 配信時間帯

制限なし（プログラム実行中は常時利用可能）

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

- チャンネルに送信されるデータには機密情報（認証トークン、パスワード等）が含まれる可能性があるため、購読者は適切にデータを取り扱う必要がある
- 悪意のある購読者がパフォーマンスに影響を与える可能性があるため、信頼できるコードのみが購読を行うべき
- WeakRefを使用してチャンネルを管理し、不要なメモリリークを防止

## 備考

- diagnostics_channelはNode.js v15.1.0で追加された
- 同期実行のため、購読者のパフォーマンスはアプリケーション全体に影響する
- AsyncLocalStorageとの連携により、リクエストコンテキストの追跡が可能
- 標準で多数の組み込みチャンネルが提供されている（http.client.*, http.server.*, net.*, etc.）

---

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

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

### 推奨読解順序

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

まず、チャンネル管理のためのデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | diagnostics_channel.js | `lib/diagnostics_channel.js` | 38-65行目：WeakRefMapクラスの実装、チャンネルの弱参照管理 |

**読解のコツ**: WeakRefとFinalizationRegistryを使用したメモリ管理パターンを理解する。

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

チャンネルの取得・作成のエントリーポイントを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | diagnostics_channel.js | `lib/diagnostics_channel.js` | 227-236行目：channel()関数、チャンネルの取得または新規作成 |

**主要処理フロー**:
1. **227行目**: channel()関数のエントリーポイント
2. **228-229行目**: 既存チャンネルの検索
3. **231-233行目**: チャンネル名の型検証
4. **235行目**: 新規Channelインスタンスの作成

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

非アクティブなチャンネルの基本実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | diagnostics_channel.js | `lib/diagnostics_channel.js` | 181-223行目：Channelクラス、非アクティブ時の軽量実装 |

**主要処理フロー**:
- **182-188行目**: コンストラクタ、チャンネル名の設定とマップへの登録
- **196-199行目**: subscribe()、アクティブ化してから購読
- **214-217行目**: hasSubscribersプロパティ（常にfalse）
- **218行目**: publish()、何もしない空実装

#### Step 4: ActiveChannelクラスを理解する

購読者が存在する場合のアクティブなチャンネル実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | diagnostics_channel.js | `lib/diagnostics_channel.js` | 105-179行目：ActiveChannelクラス、購読者管理とメッセージ配信 |

**主要処理フロー**:
- **106-111行目**: subscribe()、購読者の追加
- **113-126行目**: unsubscribe()、購読者の削除
- **147-149行目**: hasSubscribersプロパティ（常にtrue）
- **151-163行目**: publish()、全購読者へのメッセージ配信

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

```
dc.channel('name')
    │
    └─ channel() [227行目]
           │
           ├─ channels.get(name) [228行目]
           │      └─ WeakRefMap.get()
           │
           └─ new Channel(name) [235行目]
                  └─ channels.set(name, this) [187行目]

channel.subscribe(callback)
    │
    ├─ [非アクティブ時] Channel.subscribe() [196-199行目]
    │      ├─ markActive(this) [67-72行目]
    │      │      └─ ObjectSetPrototypeOf → ActiveChannel
    │      └─ this.subscribe(callback) [再帰呼び出し]
    │
    └─ [アクティブ時] ActiveChannel.subscribe() [106-111行目]
           └─ ArrayPrototypePush(_subscribers, callback)

channel.publish(data)
    │
    ├─ [非アクティブ時] Channel.publish() [218行目]
    │      └─ 何もしない
    │
    └─ [アクティブ時] ActiveChannel.publish() [151-163行目]
           └─ 各購読者にonMessage(data, name)を呼び出し
```

### データフロー図

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

Publisher         Channel (Active)              Subscriber
    │                   │                           │
    ▼                   ▼                           ▼
publish(data) ───▶ hasSubscribers? ───▶ onMessage(data, name)
                       │
                       ▼
               _subscribers配列を
                   順次実行
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| diagnostics_channel.js | `lib/diagnostics_channel.js` | ソース | diagnostics_channelモジュールのメイン実装 |
| validators.js | `lib/internal/validators.js` | ソース | 引数検証ユーティリティ |
| errors.js | `lib/internal/errors.js` | ソース | エラーコード定義 |
| util.js | `lib/internal/util.js` | ソース | WeakReferenceクラス |
