# 機能設計書 4-KeyedPoll

## 概要

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

### 本機能の処理概要

**業務上の目的・背景**：
`Poll`クラスは複数のAwaitableを完了順に取得する機能を提供するが、元のコレクションのキーは保持されない。しかし、実務では「どの処理の結果か」を識別する必要がある場合が多い。`KeyedPoll`は`Poll`の機能に加えて、`KeyedTraversable`のキーを保持し、結果と共にキーを返却する機能を提供する。これにより、複数のバックエンドへのリクエストで、どのバックエンドからの応答かを識別しながら完了順に処理できる。

**機能の利用シーン**：
- 複数のキャッシュサーバーへの同時リクエストで、応答元を識別したい場合
- 複数のAPIエンドポイントへの並列リクエストで、エンドポイント別に結果を処理したい場合
- 名前付きの非同期タスクを完了順に処理し、タスク名と結果を紐付けたい場合
- dictやMapの各エントリに対する並列処理で、キーを保持したい場合

**主要な処理内容**：
1. `from()`静的メソッドでKeyedTraversableからKeyedPollを作成
2. `add()`メソッドでキーとAwaitableのペアを追加
3. `addMulti()`メソッドで複数のキー付きAwaitableを一括追加
4. `foreach await as $k => $v`構文でキーと値のペアを完了順に取得

**関連システム・外部連携**：
- `BasePoll`抽象クラスを継承
- `ConditionNode`を内部で使用
- `AsyncKeyedIterator`インターフェースを実装

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

## 関連画面

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

## 機能種別

非同期イベント処理 / 並列処理制御 / キー付きイテレーション

## 入力仕様

### 入力パラメータ

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

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

#### add()メソッド

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| key | Tk | Yes | Awaitableに紐付けるキー | 型パラメータTkに適合 |
| awaitable | Awaitable&lt;Tv&gt; | Yes | 追加するAwaitable | Awaitable |

#### addMulti()メソッド

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

### 入力データソース

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

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| AsyncKeyedIterator要素 | (Tk, Tv) | キーと値のペア（完了順に返却） |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. KeyedPoll::from($awaitables)でインスタンス作成
   └─ 各キーとawaitableのペアに対してConditionNodeを作成
   └─ 完了時に(key, result)タプルを通知するよう設定
2. foreach ($poll await as $k => $v)でイテレーション開始
   └─ next()メソッドが呼び出される
3. next()で次の完了を待機
   └─ BasePollのnext()を呼び出し
   └─ (Tk, Tv)タプルを返却
4. Awaitableが完了
   └─ waitForThenNotify()が(key, result)を通知
5. 全てのAwaitableが完了
   └─ next()がnullを返却
   └─ イテレーション終了
```

### フローチャート

```mermaid
flowchart TD
    A[KeyedPoll::from開始] --> B[KeyedTraversableをイテレート]
    B --> C[各key, awaitableペアを処理]
    C --> D[addImpl でキー付きノード作成]
    D --> E{foreach await開始}
    E --> F[next呼び出し]
    F --> G{完了待ちノードあり?}
    G -->|No| H[イテレーション終了]
    G -->|Yes| I[waitForNotificationAsync]
    I --> J[Awaitable完了]
    J --> K[key, resultタプル通知]
    K --> L[k => vとしてyield]
    L --> E
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | キー保持 | 入力時のキーが結果と共に保持される | 常時 |
| BR-02 | 完了順序 | 結果はAwaitableの完了順に返却される | 常時 |
| BR-03 | キーの一意性 | 同一キーで複数追加した場合、それぞれ独立して返却される | 同一キー使用時 |
| BR-04 | 動的追加 | イテレーション中でもadd/addMultiで追加可能 | イテレーション完了前 |
| BR-05 | イテレーション終了後の追加禁止 | イテレーション完了後のadd/addMultiはinvariant違反 | イテレーション完了後 |

### 計算ロジック

Pollと同様のConditionNodeリンクリスト構造を使用するが、各ノードには`(Tk, Tv)`タプルが格納される。

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

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

## エラー処理

### エラーケース一覧

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

### リトライ仕様

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

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

該当なし

## パフォーマンス要件

Pollと同様の警告が適用される：
- 重量級メカニズムのため、適切なユースケースでのみ使用
- 高レイテンシの投機的リクエストに適する
- 大量の中間結果の並び替えには不向き

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

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

## 備考

- `Poll`はキーが不要な場合に使用
- キーの型`Tk`は任意の型を使用可能
- 同一キーの重複は許可されるが、識別目的ではキーの一意性を保つことを推奨

---

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

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

### 推奨読解順序

#### Step 1: KeyedPollクラスの構造を理解する

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

**読解のコツ**: `KeyedPoll<Tk, Tv>`は2つの型パラメータを持ち、`Tk`がキーの型、`Tv`が値の型である。

**主要な構造**:
- **29行目**: `extends BasePoll<Tk, Tv>` - BasePollを継承
- **31行目**: `implements AsyncKeyedIterator<Tk, Tv>` - キー付きイテレータを実装

#### Step 2: from()メソッドを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | KeyedPoll.php | `src/async/KeyedPoll.php` | from()メソッド（37-41行目） |

**主要処理フロー**:
- **38行目**: `KeyedTraversable<Tk, Awaitable<Tv>>`を引数に取る
- **40行目**: `fromImpl()`に直接委譲（Pollとは異なりVectorラップ不要）

#### Step 3: add()メソッドを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | KeyedPoll.php | `src/async/KeyedPoll.php` | add()メソッド（47-49行目） |

**主要処理フロー**:
- **47行目**: `Tk $key`と`Awaitable<Tv> $awaitable`を引数に取る
- **48行目**: `addImpl($key, $awaitable)` - キーを指定して基底クラスに委譲

#### Step 4: addMulti()メソッドを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | KeyedPoll.php | `src/async/KeyedPoll.php` | addMulti()メソッド（52-56行目） |

**主要処理フロー**:
- **53行目**: `KeyedTraversable<Tk, Awaitable<Tv>>`を引数に取る
- **55行目**: `addMultiImpl()`に直接委譲

#### Step 5: 基底クラスでのキー処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | BasePoll.php | `src/async/BasePoll.php` | addImpl()とwaitForThenNotify()のキー処理 |

**主要処理フロー**:
- **84行目**: `addImpl(Tk $key, ...)`でキーを受け取る
- **94行目**: `waitForThenNotify($key, $awaitable)` - キーを渡す
- **140行目**: `succeed(tuple($key, $result))` - (key, value)タプルを通知

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

```
KeyedPoll::from($awaitables)
    |
    +-- BasePoll::fromImpl($awaitables)
            |
            +-- new static()
            +-- addMultiImpl($awaitables)
                    |
                    +-- foreach ($awaitables as $key => $awaitable)
                            |
                            +-- addImpl($key, $awaitable)
                                    |
                                    +-- $lastAdded->addNext()
                                    +-- waitForThenNotify($key, $awaitable)

KeyedPoll::add($key, $awaitable)
    |
    +-- BasePoll::addImpl($key, $awaitable)

foreach ($poll await as $k => $v)
    |
    +-- BasePoll::next()
            |
            +-- waitForNotificationAsync()
                    |
                    +-- return tuple($key, $result) // (Tk, Tv)
```

### データフロー図

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

KeyedTraversable<Tk, Awaitable<Tv>>
        |
        v
    KeyedPoll::from()
        |
        v
    ┌──────────────────────────────────────────────────────┐
    │ foreach ($awaitables as $key => $awaitable)          │
    │                                                      │
    │   ConditionNode                                      │
    │   - 各nodeに(key, awaitable)を紐付け                 │
    │   - 完了時にtuple($key, $result)を通知               │
    └──────────────────────────────────────────────────────┘
        |
        v
    foreach await as $k => $v
        |
        v
    ┌──────────────────────────────────────────────────────┐
    │ next()                                               │
    │   - waitForNotificationAsync()                       │
    │   - tuple(Tk, Tv)を返却                              │
    │   - $k => $vとしてyield                              │
    └──────────────────────────────────────────────────────┘
        |
        v
    ($k: Tk, $v: Tv) 完了順に順次返却
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| KeyedPoll.php | `src/async/KeyedPoll.php` | ソース | KeyedPollクラスの実装 |
| BasePoll.php | `src/async/BasePoll.php` | ソース | Poll/KeyedPollの基底クラス |
| Poll.php | `src/async/Poll.php` | ソース | キーなしバリアント |
| ConditionNode.php | `src/async/ConditionNode.php` | ソース | リンクリストノード |
| PollTest.php | `tests/async/PollTest.php` | テスト | KeyedPollを含むテスト（105-131行目: testKeyed） |
