# 通知設計書 17-IPC NACK通知

## 概要

本ドキュメントは、BunにおけるIPC NACK（否定応答）通知の設計を記載する。IPCハンドル否定応答を送信する機能である。

### 本通知の処理概要

IPC NACK通知は、ファイルディスクリプタ（ハンドル）を含むIPCメッセージを受信したが、ファイルディスクリプタ自体を正常に受け取れなかった場合に送信元に通知するための否定応答機能である。Node.jsのNODE_HANDLE_NACKプロトコルと互換性がある。

**業務上の目的・背景**：ファイルディスクリプタの送信は、メッセージとは別にソケットのアンシラリーデータとして送信される。タイミングの問題やシステムリソースの問題でFDの受信に失敗することがある。NACKにより、送信側は再送を試みることができ、ハンドル送信の信頼性が向上する。

**通知の送信タイミング**：受信側が`NODE_HANDLE`コマンドを含むメッセージを受信したが、ファイルディスクリプタ（incoming_fd）がnullの場合に送信される。

**通知の受信者**：ハンドル付きメッセージを送信した送信元プロセス。送信元はNACKを受信するとリトライを試みる。

**通知内容の概要**：`NODE_HANDLE_NACK`コマンドを含むJSONオブジェクト（jsonモード）またはシリアライズされたメッセージ（advancedモード）。

**期待されるアクション**：送信側はNACKを受信すると、リトライカウントを増加させ、最大3回までハンドル付きメッセージの再送を試みる。3回失敗した場合は警告を発して送信を断念する。

## 通知種別

プロセス間通信（IPC NACK応答）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期 |
| 優先度 | 高（リトライのトリガー） |
| リトライ | なし（NACK自体はリトライしない） |

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

ハンドル付きメッセージの送信元プロセスに対して直接送信。

## 通知テンプレート

### JSONモードの場合

```json
{"cmd":"NODE_HANDLE_NACK"}
```

改行文字（`\n`）で終端。

### Advancedモードの場合

```
\x02\x25\x00\x00\x00\r\x00\x00\x00\x02\x03\x00\x00\x80cmd\x10\x10\x00\x00\x80NODE_HANDLE_NACK\xff\xff\xff\xff
```

事前にシリアライズされた固定バイト列。

### 添付ファイル

なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| cmd | コマンド種別 | 固定値 "NODE_HANDLE_NACK" | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| メッセージ受信 | NODE_HANDLEコマンド受信 | incoming_fdがnull | FDが受信できなかった場合 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| FD受信成功 | incoming_fdが存在する場合はACKを送信 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[NODE_HANDLEメッセージ受信] --> B{incoming_fd != null?}
    B -->|Yes| C[ACKパケット作成]
    B -->|No| D[NACKパケット作成]
    D --> E[SendQueueにinsertMessage]
    E --> F[continueSend呼び出し]
    F --> G[NACK送信]
    G --> H[処理終了 - messageイベントは発火しない]
```

### 受信側（送信元）のフロー

```mermaid
flowchart TD
    A[NACKメッセージ受信] --> B[onAckNack呼び出し]
    B --> C{waiting_for_ack != null?}
    C -->|No| D[警告ログ出力]
    C -->|Yes| E[retry_count++]
    E --> F{retry_count < 3?}
    F -->|Yes| G[メッセージをキュー先頭に再挿入]
    G --> H[continueSend呼び出し]
    H --> I[リトライ送信]
    F -->|No| J[警告を発行]
    J --> K[waiting_for_ackをクリア]
    K --> L[continueSend呼び出し]
    L --> M[次のメッセージ送信]
```

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

### 参照テーブル一覧

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

| データ構造 | 用途 | 備考 |
|-----------|------|------|
| SendQueue.waiting_for_ack | 送信側のACK待ち状態 | SendHandle型 |
| SendQueue.retry_count | リトライ回数 | u32型、最大3 |
| SendQueue.incoming_fd | 受信したFD | bun.FileDescriptor型 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| リトライ上限超過 | retry_count >= 3 | 警告を発行してメッセージを破棄 |
| NACK不一致 | waiting_for_ackがnullなのにNACK受信 | 警告ログのみ |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 最大3回（MAX_HANDLE_RETRANSMISSIONS） |
| リトライ間隔 | 即時 |
| リトライ対象エラー | NACK受信時 |

## 配信設定

### レート制限

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

### 配信時間帯

IPCチャネル開通中は常時送信可能

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

- NACKはIPC内部プロトコルの一部であり、外部からのアクセスは不可
- リトライ上限により、無限ループや DoS を防止

## 備考

- Node.jsのNODE_HANDLE_NACKプロトコルとの完全互換
- リトライ上限超過時は `SentHandleNotReceivedWarning` 警告が発行される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ipc.zig | `src/bun.js/ipc.zig` | advanced.getNackPacket()（138-140行目） |
| 1-2 | ipc.zig | `src/bun.js/ipc.zig` | json.getNackPacket()（179-181行目） |
| 1-3 | ipc.zig | `src/bun.js/ipc.zig` | MAX_HANDLE_RETRANSMISSIONS（947行目） |
| 1-4 | ipc.zig | `src/bun.js/ipc.zig` | SendQueue.retry_count（447行目） |

**読解のコツ**: getNackPacket()はgetAckPacket()と同様に固定バイト列を返す。NACKとACKの違いはcmdフィールドの値のみ。

#### Step 2: NACK送信処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ipc.zig | `src/bun.js/ipc.zig` | handleIPCMessage関数内のNODE_HANDLEケース（1101-1118行目） |

**主要処理フロー**:
1. **1103-1105行目**: incoming_fdがnullの場合
2. **1107行目**: getNackPacket()でNACKパケット取得
3. **1118行目**: ACKでない場合は早期リターン（messageイベントなし）

#### Step 3: NACK受信・リトライ処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ipc.zig | `src/bun.js/ipc.zig` | onAckNack関数のnackケース（655-676行目） |

**主要処理フロー**:
- **655行目**: ack_nack == .nack の分岐
- **657行目**: retry_count のインクリメント
- **658-664行目**: リトライ処理
- **667-673行目**: リトライ上限超過時の警告発行

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

```
受信側: NODE_HANDLEメッセージ受信（FD受信失敗）
    │
    └─ handleIPCMessage()
           │
           ├─ icmd == .handle
           │      │
           │      ├─ ack = (incoming_fd != null) → false
           │      │
           │      ├─ getNackPacket()
           │      │      ├─ json: '{"cmd":"NODE_HANDLE_NACK"}\n'
           │      │      └─ advanced: バイナリ列
           │      │
           │      ├─ SendHandle作成 (callbacks = .ack_nack)
           │      │
           │      ├─ insertMessage() → キュー先頭に挿入
           │      │
           │      ├─ continueSend() → NACK送信
           │      │
           │      └─ return (messageイベントなし)

送信側: NACKメッセージ受信
    │
    └─ handleIPCMessage()
           │
           └─ icmd == .nack
                  │
                  └─ onAckNack(globalThis, .nack)
                         │
                         ├─ retry_count++
                         │
                         ├─ retry_count < MAX_HANDLE_RETRANSMISSIONS (3)?
                         │      │
                         │      ├─ Yes: リトライ
                         │      │      ├─ data.cursor = 0
                         │      │      ├─ insertMessage() → キュー先頭に再挿入
                         │      │      ├─ waiting_for_ack = null
                         │      │      └─ continueSend() → 再送信
                         │      │
                         │      └─ No: 断念
                         │             ├─ emitWarning("SentHandleNotReceivedWarning")
                         │             ├─ item.complete() → コールバック呼び出し
                         │             ├─ waiting_for_ack = null
                         │             └─ continueSend() → 次のメッセージ送信
```

### データフロー図

```
[送信プロセス]                           [受信プロセス]

ハンドル付き                              NODE_HANDLE
メッセージ送信 ────────────────────────▶ 受信（FDなし）
    │                                        │
    │                                        │
    ▼                                        ▼
waiting_for_ack                         incoming_fd == null
に保存                                       │
    │                                        │
    │                                        ▼
    │                                   NACKパケット生成
    │                                        │
    │                   NACK                 │
    ◀──────────────────────────────────────
    │
    ▼
onAckNack(.nack)
    │
    ▼
retry_count < 3?
    │
    ├─ Yes ──▶ リトライ送信 ──────────────▶ [受信プロセス]
    │
    └─ No ───▶ 警告発行
               waiting_for_ack クリア
               次のメッセージ送信
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ipc.zig | `src/bun.js/ipc.zig` | ソース | NACK処理の主実装、リトライロジック |
| IPC.cpp | `src/bun.js/bindings/IPC.cpp` | ソース | ipcParse/ipcSerializeのC++実装 |
