# 機能設計書 3-Poll

## 概要

本ドキュメントは、Hack Standard Library (HSL) の非同期処理モジュール `HH\Lib\Async\Poll` クラスの機能設計書である。

### 本機能の処理概要

**業務上の目的・背景**：
複数の非同期処理を同時に実行し、完了した順に結果を取得したい場合がある。従来の`await`では全ての処理が完了するまで待機するか、個別に待機する必要があった。`Poll`クラスは、Unix/POSIXの`poll()`/`select()`/`epoll()`に相当する機能を提供し、複数のAwaitableの結果を完了順に非同期イテレータとして取得できる。これにより、レスポンスタイムの短縮や、ストリーミング形式での結果出力が可能になる。

**機能の利用シーン**：
- 複数のバックエンドへの並列リクエストで、最初に応答したものから処理したい場合
- 複数のGraphQLクエリをストリーミング形式でクライアントに返却したい場合
- 分散キャッシュ（memcache等）への投機的リクエストで、最初の結果を採用したい場合
- ページレット等の並列レンダリングで、完了順に出力をフラッシュしたい場合

**主要な処理内容**：
1. `from()`静的メソッドで複数のAwaitableからPollを作成
2. `add()`メソッドで追加のAwaitableを動的に追加
3. `addMulti()`メソッドで複数のAwaitableを一括追加
4. `foreach await`構文でAsyncIteratorとして結果を順次取得
5. `waitUntilEmptyAsync()`で全ての処理完了を待機（結果は破棄）

**関連システム・外部連携**：
- `BasePoll`抽象クラスを継承
- `ConditionNode`を内部で使用してイベント通知を実現
- `Vec\from_async`を使用して複数の通知を集約

**権限による制御**：該当なし（ライブラリ機能のため）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 画面なし | - | 本プロジェクトはUIを持たないライブラリのため該当なし |

## 機能種別

非同期イベント処理 / 並列処理制御

## 入力仕様

### 入力パラメータ

#### from()静的メソッド

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| awaitables | Traversable&lt;Awaitable&lt;Tv&gt;&gt; | Yes | 監視対象のAwaitableコレクション | Traversable |

#### add()メソッド

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| awaitable | Awaitable&lt;Tv&gt; | Yes | 追加するAwaitable | Awaitable |

#### addMulti()メソッド

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| awaitables | Traversable&lt;Awaitable&lt;Tv&gt;&gt; | Yes | 追加するAwaitableコレクション | Traversable |

### 入力データソース

- プログラムから直接呼び出し（API）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| AsyncIterator要素 | Tv | 完了したAwaitableの結果値（完了順に返却） |
| waitUntilEmptyAsync | Awaitable&lt;void&gt; | 全完了を待機（結果は破棄） |

### 出力先

- foreach await構文による順次取得
- 呼び出し元プログラムへの戻り値

## 処理フロー

### 処理シーケンス

```
1. Poll::from($awaitables)でインスタンス作成
   └─ 各awaitableに対してConditionNodeを作成
   └─ 完了時に通知するasyncラッパーを設定
2. foreach ($poll await as $value)でイテレーション開始
   └─ next()メソッドが呼び出される
3. next()で次の完了を待機
   └─ lastAwaitedを次のノードに進める
   └─ ConditionNode::waitForNotificationAsync()で待機
4. Awaitableが完了
   └─ waitForThenNotify()が結果を取得
   └─ lastNotifiedを進めて結果を通知
5. 全てのAwaitableが完了
   └─ next()がnullを返却
   └─ イテレーション終了
```

### フローチャート

```mermaid
flowchart TD
    A[Poll::from開始] --> B[各Awaitableにノード作成]
    B --> C[notifiersにラッパーを追加]
    C --> D{foreach await開始}
    D --> E[next呼び出し]
    E --> F{lastAwaited->getNext がnull?}
    F -->|Yes| G[イテレーション終了]
    F -->|No| H[waitForNotificationAsync]
    H --> I[Awaitable完了]
    I --> J[結果を通知]
    J --> K[値をyield]
    K --> D
    G --> L[lastAdded/lastNotified をnullに]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 完了順序 | 結果はAwaitableの完了順に返却される（追加順ではない） | 常時 |
| BR-02 | 例外伝播 | Awaitable内で発生した例外はイテレーション時にスローされる | Awaitableが例外をスロー |
| BR-03 | 動的追加 | イテレーション中でもadd/addMultiで追加可能 | イテレーション完了前 |
| BR-04 | イテレーション終了後の追加禁止 | イテレーション完了後のadd/addMultiはinvariant違反 | イテレーション完了後 |
| BR-05 | キー非保持 | Traversableのキーは保持されない（KeyedPollを使用） | Poll使用時 |

### 計算ロジック

**ConditionNodeリンクリスト**：
```
head -> node1 -> node2 -> node3 -> null
  ^       ^        ^        ^
  |       |        |        |
lastAwaited   (完了待ち)   lastAdded
         lastNotified
```

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

該当なし（メモリ上での状態管理のみ）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| InvariantViolation | 実行時エラー | イテレーション完了後にadd/addMulti | イテレーション完了前に追加する |
| InvariantViolation | 実行時エラー | next()を完了後に再度呼び出し | 一度のイテレーションのみ |
| Exception | ユーザー例外 | Awaitable内で例外発生 | イテレーション側でcatchして処理 |

### リトライ仕様

本機能自体にリトライ機構は組み込まれていない。

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

該当なし

## パフォーマンス要件

**警告**：Pollは重量級のメカニズムであり、以下の場合は使用を避けるべき：
- 大量の中間結果の並び替え（CPU負荷が高い）
- 処理レイテンシが10ms未満のバックエンド呼び出し
- 無視される処理のCPUコストが無視できない場合

推奨ユースケース：
- 高レイテンシ（10ms以上）の投機的リクエスト
- 比較的少数の高レベル結果のストリーミング出力

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

該当なし（ライブラリ内部機能のため）

## 備考

- `KeyedPoll`はキーを保持するバリアント
- `waitUntilEmptyAsync()`は副作用のためにAwaitableを実行する場合に便利
- BasePollクラスのソースコード冒頭に詳細な警告コメントがある

---

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

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

### 推奨読解順序

#### Step 1: 基底クラスのデータ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | BasePoll.php | `src/async/BasePoll.php` | クラス定義とプロパティ（57行目、70-73行目） |

**読解のコツ**: BasePollはConditionNodeのリンクリストを使用して、完了通知を順序付けている。

**主要なプロパティ**:
- **70行目**: `$lastAdded` - 最後に追加されたノード
- **71行目**: `$lastNotified` - 最後に通知されたノード
- **72行目**: `$lastAwaited` - 最後にawaitされたノード
- **73行目**: `$notifiers` - 全てのAwaitableを追跡するAwaitable

#### Step 2: Pollクラスの実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Poll.php | `src/async/Poll.php` | クラス定義とインターフェース（22行目） |

**主要処理フロー**:
- **22行目**: `AsyncIterator<Tv>`を実装
- **28-30行目**: `from()` - Vectorでラップして基底クラスに委譲
- **33-35行目**: `add()` - nullキーで基底クラスに委譲
- **41-43行目**: `addMulti()` - Vectorでラップして基底クラスに委譲

#### Step 3: 基底クラスの追加処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | BasePoll.php | `src/async/BasePoll.php` | addImpl()とaddMultiImpl()（84-130行目） |

**主要処理フロー**:
- **85-88行目**: イテレーション完了後の追加チェック
- **91行目**: `$lastAdded->addNext()` - 新しいノードを作成
- **94行目**: `waitForThenNotify()` - 完了時の通知設定
- **96-101行目**: `$notifiers`の更新（concurrentブロック）

#### Step 4: 通知処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | BasePoll.php | `src/async/BasePoll.php` | waitForThenNotify()（132-146行目） |

**主要処理フロー**:
- **137行目**: `await $awaitable` - 完了を待機
- **138-140行目**: 成功時: `$lastNotified`を進めて`succeed()`
- **141-144行目**: 失敗時: `$lastNotified`を進めて`fail()`

#### Step 5: イテレーション処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | BasePoll.php | `src/async/BasePoll.php` | next()メソッド（148-163行目） |

**主要処理フロー**:
- **154行目**: `$lastAwaited->getNext()` - 次のノードを取得
- **155-159行目**: nullの場合はイテレーション終了
- **162行目**: `waitForNotificationAsync()` - 通知を待機

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

```
Poll::from($awaitables)
    |
    +-- BasePoll::fromImpl()
            |
            +-- new static() [コンストラクタ]
            |       |
            |       +-- new ConditionNode() [headノード]
            |
            +-- addMultiImpl($awaitables)
                    |
                    +-- foreach $awaitables
                            |
                            +-- $lastAdded->addNext()
                            +-- waitForThenNotify($key, $awaitable)

foreach ($poll await as $value)
    |
    +-- BasePoll::next()
            |
            +-- $lastAwaited->getNext()
            +-- ConditionNode::waitForNotificationAsync($notifiers)
                    |
                    +-- [完了時] waitForThenNotify内でsucceed()
                            |
                            +-- tuple($key, $result)を通知
```

### データフロー図

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

Traversable<Awaitable<Tv>>
        |
        v
    Poll::from()
        |
        v
    ┌─────────────────────────────────────────────────┐
    │ ConditionNode リンクリスト                       │
    │                                                 │
    │ head -> node1 -> node2 -> node3 -> null        │
    │                                                 │
    │ 各nodeにAwaitableを紐付け                        │
    │ 完了時にsucceed()で通知                          │
    └─────────────────────────────────────────────────┘
        |
        v
    foreach await
        |
        v
    ┌─────────────────────────────────────────────────┐
    │ next()                                          │
    │   - lastAwaitedを進める                          │
    │   - waitForNotificationAsync()で待機             │
    │   - 完了順に(key, value)タプルを返却             │
    └─────────────────────────────────────────────────┘
        |
        v
    Tv (完了順に順次返却)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Poll.php | `src/async/Poll.php` | ソース | Pollクラスの実装 |
| BasePoll.php | `src/async/BasePoll.php` | ソース | Poll/KeyedPollの基底クラス |
| ConditionNode.php | `src/async/ConditionNode.php` | ソース | リンクリストノード |
| Condition.php | `src/async/Condition.php` | ソース | ConditionNodeの基底クラス |
| KeyedPoll.php | `src/async/KeyedPoll.php` | ソース | キー付きバリアント |
| PollTest.php | `tests/async/PollTest.php` | テスト | Pollの単体テスト |
