# 機能設計書 107-node:cluster

## 概要

本ドキュメントは、BunにおけるNode.js互換の`node:cluster`モジュールの機能設計を記述したものである。このモジュールは、マルチプロセスによる並列処理機能を提供する。

### 本機能の処理概要

`node:cluster`モジュールは、単一のNode.js/Bunプロセスを複数のワーカープロセスにフォークし、同一ポートでリクエストを処理する機能を提供する。プライマリ（マスター）プロセスがワーカーを管理し、ラウンドロビンまたはOS任せでリクエストを分配する。

**業務上の目的・背景**：Node.js/Bunはシングルスレッドで動作するため、マルチコアCPUを効率的に活用するにはマルチプロセス化が必要である。`cluster`モジュールは、アプリケーションコードを大きく変更することなく、複数のCPUコアでリクエストを並列処理できるようにする。

**機能の利用シーン**：
- HTTPサーバーの負荷分散
- CPUバウンドな処理の並列化
- 高可用性（ワーカーが死んでも自動再起動）
- ゼロダウンタイムデプロイ

**主要な処理内容**：
1. `cluster.fork()`: ワーカープロセスの生成
2. `cluster.workers`: ワーカー一覧の管理
3. ラウンドロビン/OSスケジューリングによるリクエスト分配
4. プライマリ-ワーカー間のIPC通信

**関連システム・外部連携**：
- `child_process.fork()`: ワーカープロセスの生成
- IPC（Inter-Process Communication）: プロセス間通信

**権限による制御**：
- `cluster.settings.uid`/`gid`でワーカーの実行ユーザーを指定可能

## 関連画面

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

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

## 機能種別

プロセス管理 / 負荷分散

## 入力仕様

### 入力パラメータ

#### `cluster.setupPrimary(settings)`

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| settings | object | No | クラスター設定 | - |
| settings.exec | string | No | ワーカースクリプト（デフォルト: process.argv[1]） | - |
| settings.args | string[] | No | ワーカー引数 | - |
| settings.execArgv | string[] | No | Bun/Node固有引数 | - |
| settings.cwd | string | No | ワーカーの作業ディレクトリ | - |
| settings.silent | boolean | No | 標準出力を親に転送しない | - |
| settings.uid | number | No | ワーカーの実行UID | - |
| settings.gid | number | No | ワーカーの実行GID | - |
| settings.serialization | string | No | シリアライズ方式 | - |

#### `cluster.fork([env])`

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| env | object | No | ワーカーに渡す追加環境変数 | - |

### 入力データソース

- 関数呼び出しの引数
- `process.env.NODE_CLUSTER_SCHED_POLICY`: スケジューリングポリシー

## 出力仕様

### 出力データ

#### プライマリプロセスのプロパティ

| プロパティ名 | 型 | 説明 |
|-------------|-----|------|
| cluster.isPrimary | boolean | true |
| cluster.isWorker | boolean | false |
| cluster.isMaster | boolean | true（非推奨エイリアス） |
| cluster.workers | object | ワーカー一覧 { id: Worker } |
| cluster.settings | object | クラスター設定 |
| cluster.SCHED_RR | number | ラウンドロビン（2） |
| cluster.SCHED_NONE | number | OS任せ（1） |

#### ワーカープロセスのプロパティ

| プロパティ名 | 型 | 説明 |
|-------------|-----|------|
| cluster.isPrimary | boolean | false |
| cluster.isWorker | boolean | true |
| cluster.worker | Worker | 自身のWorkerオブジェクト |

#### Workerオブジェクト

| プロパティ/メソッド | 型 | 説明 |
|-------------------|-----|------|
| id | number | ワーカーID |
| process | ChildProcess | 子プロセスオブジェクト |
| state | string | 状態（'online', 'dead'等） |
| send(message) | function | メッセージ送信 |
| kill([signal]) | function | ワーカー終了 |
| disconnect() | function | IPC切断 |
| isConnected() | boolean | IPC接続中か |
| isDead() | boolean | プロセスが終了したか |

### 出力先

- イベントリスナーへのコールバック
- プロパティへの直接アクセス

## 処理フロー

### 処理シーケンス（プライマリ）

```
1. クラスター設定
   └─ cluster.setupPrimary(settings)
2. ワーカー生成
   └─ cluster.fork() × CPUコア数
3. イベント監視
   └─ cluster.on('online', ...)
   └─ cluster.on('exit', ...)
4. ラウンドロビン分配
   └─ RoundRobinHandle経由でリクエスト分配
```

### 処理シーケンス（ワーカー）

```
1. 初期化
   └─ _setupWorker()でワーカー設定
2. オンライン通知
   └─ send({ act: 'online' })
3. サーバー起動
   └─ _getServer()でハンドル取得
4. リクエスト受信
   └─ onconnection(message, handle)
```

### フローチャート

```mermaid
flowchart TD
    A[アプリ起動] --> B{isPrimary?}
    B -->|Yes| C[setupPrimary]
    B -->|No| D[_setupWorker]
    C --> E[fork × N]
    E --> F[ワーカー起動]
    F --> G[online通知]
    D --> G
    G --> H[サーバーリッスン]
    H --> I{RR or NONE?}
    I -->|RR| J[RoundRobinHandle]
    I -->|NONE| K[OS分配]
    J --> L[リクエスト分配]
    K --> L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-701 | スケジューリングポリシー | NODE_CLUSTER_SCHED_POLICYで設定 | 起動時 |
| BR-702 | デフォルトRR | Linux/macOSはラウンドロビンがデフォルト | SCHED_POLICY未設定時 |
| BR-703 | NODE_UNIQUE_ID | ワーカー識別用環境変数 | ワーカー起動時 |
| BR-704 | ワーカー自動削除 | disconnect後にexitでworkersから削除 | ワーカー終了時 |

### 計算ロジック

**ラウンドロビン分配**：
- `RoundRobinHandle`クラスでワーカーを順番に選択
- 各ワーカーに均等にリクエストを分配

**ワーカーID**：
- プライマリで`++ids`でインクリメンタルに割り当て

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

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

## エラー処理

### エラーケース一覧

| エラーケース | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| Bad cluster.schedulingPolicy | Error | 不正なポリシー値 | SCHED_RRまたはSCHED_NONE |
| ERR_INTERNAL_ASSERTION | Error | 内部不整合 | バグ報告 |
| Resource leak detected | Error | ハンドルリーク | バグ報告 |

### リトライ仕様

- ワーカーがクラッシュした場合、アプリケーション側で`exit`イベントを監視してfork()を再実行することで再起動可能

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

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

## パフォーマンス要件

- ラウンドロビン分配は均等な負荷分散を実現
- IPC通信のオーバーヘッドを考慮

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

- `uid`/`gid`設定で権限を制限可能
- ワーカー間でメモリは共有されない（プロセス分離）

## 備考

- `isMaster`は`isPrimary`の非推奨エイリアス
- `setupMaster`は`setupPrimary`の非推奨エイリアス
- Windowsではラウンドロビンのパフォーマンスに課題があるが、Bunでは現在RRを使用

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | cluster.ts | `src/js/node/cluster.ts` | モジュールのエントリーポイント |

**読解のコツ**:
- **3-5行目**: isPrimaryで分岐してprimary/childをロード
- **10-20行目**: `initializeClusterIPC`でワーカー初期化

#### Step 2: プライマリの実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | primary.ts | `src/js/internal/cluster/primary.ts` | プライマリプロセスの実装 |

**主要処理フロー**:
1. **16-32行目**: クラスターオブジェクトの初期化
2. **37-48行目**: スケジューリングポリシーの決定
3. **50-70行目**: `setupPrimary`で設定を初期化
4. **79-102行目**: `createWorkerProcess`でワーカー生成
5. **122-150行目**: `fork`でワーカーを作成・イベント登録

#### Step 3: ワーカーの実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | child.ts | `src/js/internal/cluster/child.ts` | ワーカープロセスの実装 |

**主要処理フロー**:
- **20-27行目**: クラスターオブジェクトの初期化（isWorker=true）
- **28-58行目**: `_setupWorker`でワーカーを設定
- **61-100行目**: `_getServer`でサーバーハンドルを取得

#### Step 4: ラウンドロビン分配を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | RoundRobinHandle.ts | `src/js/internal/cluster/RoundRobinHandle.ts` | ラウンドロビン実装 |

**読解のコツ**:
- ワーカーのキューを管理
- 順番にリクエストを分配

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

```
[プライマリ]
    │
    ├─ cluster.setupPrimary(settings)
    │      └─ cluster.settings = {...}
    │
    ├─ cluster.fork(env)
    │      └─ createWorkerProcess(id, env)
    │             └─ child_process.fork(exec, args, options)
    │                    └─ new Worker({ id, process })
    │
    └─ RoundRobinHandle
           └─ handle.add(worker)
           └─ handle.distribute(message, handle)

[ワーカー]
    │
    ├─ _setupWorker()
    │      └─ new Worker({ id, process })
    │      └─ onInternalMessage(worker, onmessage)
    │      └─ send({ act: 'online' })
    │
    └─ _getServer(obj, options, cb)
           └─ send({ act: 'queryServer', ... })
           └─ rr(reply, ...) or shared(reply, ...)
```

### データフロー図

```
[プライマリ]                   [IPC]                      [ワーカー]

fork() ────────────────────────▶ NODE_UNIQUE_ID ─────────▶ _setupWorker()
                                                                │
cluster.on('online') ◀───────────────────────────────── send({act:'online'})
                                                                │
                               newconn ──────────────────▶ onconnection()
                               (handle)                         │
                                                                ▼
                                                         リクエスト処理
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| cluster.ts | `src/js/node/cluster.ts` | ソース | エントリーポイント |
| primary.ts | `src/js/internal/cluster/primary.ts` | ソース | プライマリ実装 |
| child.ts | `src/js/internal/cluster/child.ts` | ソース | ワーカー実装 |
| Worker.ts | `src/js/internal/cluster/Worker.ts` | ソース | Workerクラス |
| RoundRobinHandle.ts | `src/js/internal/cluster/RoundRobinHandle.ts` | ソース | ラウンドロビン |
| isPrimary.ts | `src/js/internal/cluster/isPrimary.ts` | ソース | プライマリ判定 |
| node_cluster_binding.zig | `src/bun.js/node/node_cluster_binding.zig` | ソース | Zigバインディング |
