← 全体レポートに戻る

局所トレーサビリティビュー グループ: HERD

生成日時: 2026-03-13 23:59:51

対象アイテム

5

REQ

1

ARCH

1

SPEC

1

TST

1

IMPL

1

レビュー済

5/5

Suspect

0

グループ

HERD
アイテム: REQ013 ARCH013 SPEC023 TST023 IMPL023

トレーサビリティマトリクス

✓=レビュー済 ○=未レビュー ⚠=Suspect(複数同時表示あり。IDクリックで詳細へ)

グループREQARCHSPECTSTIMPLADR
HERDREQ013
同一キャッシュキーに対する並行リクエストを直列化し、1つのリクエストのみが関数を実行し、他のリクエストはその結果を共有することで重複実行を防止できること。
ARCH013
## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|管理要求| B[CacheManager] B -...
SPEC023
## インターフェース ```python class CacheManager: def herd_sync( self, key:...
TST023
## 目的 Thundering Herd Protection は、同一キーへの並行リクエストが一斉に関数を実行する「Thundering Herd」問題を...
IMPL023
## 実装概要 `CacheManager` クラス内で、Thundering Herd(キャッシュミス時に同一キーへの 大量アクセスが同時に発生する問題)を...

カバレッジ(局所)

リンク方向カバー数カバー率未カバー
ARCH → REQ 1 / 1 100.0%
SPEC → ARCH 1 / 1 100.0%
TST → SPEC 1 / 1 100.0%
IMPL → SPEC 1 / 1 100.0%
ADR → REQ 0 / 1 0.0% REQ013

アイテム詳細

REQ013 REQ {h(g)} ✓ レビュー済

同一キャッシュキーに対する並行リクエストを直列化し、1つのリクエストのみが関数を実行し、他のリクエストはその結果を共有することで重複実行を防止できること。

親:

子: ARCH013

ARCH013 ARCH {h(g)} ✓ レビュー済

コンポーネント構成

graph TD
  A[core.Spot] -->|管理要求| B[CacheManager]
  B -->|In-flight追跡| C[_inflight Dict]
  C -->|同期待機| D[threading.Event]
  C -->|非同期待機| E[asyncio.Future]

コンポーネント責務

コンポーネント責務インターフェースCacheManagerキャッシュキーごとの実行状態管理と実行のシリアライズget_or_create_inflight()_inflight実行中のタスクを保持し、後続リクエストをイベントで待機させるExecutionStatecore.Spot待機結果の受け取りとエラーの伝播cached_run()

データフロー

sequenceDiagram
  participant T1 as Thread 1 (First)
  participant T2 as Thread 2 (Second)
  participant CM as CacheManager
  participant Fn as Target Function

  T1->>CM: get_or_create(key)
  Note over CM: 新規作成 (Executor)
  T2->>CM: get_or_create(key)
  Note over CM: 既存あり (Waiter)

  T1->>Fn: execute()
  Fn-->>T1: result
  T1->>CM: set_result(key, result)
  Note over CM: Eventをセット
  CM-->>T2: notify result
  T1-->>T1: return result
  T2-->>T2: return result

技術選定

技術領域選定理由待機機構Event / FutureOS/ランタイムレベルの待機を使用し、CPU負荷(ビジーループ)を回避スコープキャッシュキー単位異なる関数の実行は妨げず、同一入力のみを直列化安全策タイムアウト & リトライ実行者がハングした場合に、待機側がデッドロックしないよう保護

非機能要件方針

親: REQ013

子: SPEC023

SPEC023 SPEC {h(g)} ✓ レビュー済

インターフェース

class CacheManager:
    def herd_sync(
        self, key: str, serializer: Optional[SerializerProtocol] = None
    ) -> Generator[HerdWaitResult, None, None]: ...

    async def herd_async(
        self,
        key: str,
        serializer: Optional[SerializerProtocol],
        loop: asyncio.AbstractEventLoop,
        executor: Any,
    ) -> AsyncGenerator[HerdWaitResult, None]: ...

振る舞い

同時実行制御

  1. キャッシュキーをキーとした _inflight 辞書をチェックする
  2. 存在しない場合: HerdWaitResult を新規作成し、自分が「実行者 (Executor)」となる
  3. 存在する場合: 既存の HerdWaitResult を返し、自分は「待機者 (Waiter)」となる
  4. 完了通知: 実行者が処理を終えたら、HerdWaitResult の Event (または Future) をセットし、結果を共有する
  5. 自動クリーンアップ: with または async with ブロックを抜ける際、実行者は自動的に notify_and_cleanup_inflight を呼び出し、インフライト状態を解除する。これにより例外発生時も状態が残留しないことが保証される。

強参照によるインフライト状態の保持

目的

実行中のタスク状態(イベント・結果・Future)が GC によって消失しないよう、 _inflight 辞書を通じて強参照で保持する。WeakRef は使用しない。

データ構造

_inflight 辞書はキャッシュキーをキーとし、以下の3要素タプルを値として保持する:

_inflight: dict[str, tuple[threading.Event, list[asyncio.Future], list]]
要素 役割
event threading.Event 同期待機者への完了通知シグナル
futures list[asyncio.Future] 非同期待機者への結果配信チャネル
result_box list 結果の共有ボックス。[(success: bool, value: Any)] 形式

ライフサイクル

  1. 作成: herd_sync / herd_async コンテキスト開始時に _inflight にキーが存在しない場合、 _inflight_lock を保持した状態で新規タプルを挿入する
  2. 保持: 実行者が処理を完了するまで、_inflight 辞書がタプルへの唯一の管理参照を保持する。 待機者はロック取得時にタプル要素への参照を取得するが、_inflight エントリが正規の所有者である
  3. 解放: コンテキスト終了(__exit__ / __aexit__)時に notify_and_cleanup_inflight で以下の順序で解放する: a. _inflight_lock を取得し、エントリの同一性を event の identity (is) で確認する b. 辞書からエントリを削除する(del _inflight[cache_key]) c. ロックを解放した後、event.set() で同期待機者に通知する d. futures リスト内の各 asyncio.Future に結果またはエラーを伝播する

不変条件

GC安全性

_inflight 辞書は CacheManager インスタンスの属性であり、CacheManager が 生存している限り、全てのインフライトエントリは GC の対象にならない。 CacheManager 自体は Spot インスタンスが所有するため、with spot: ブロック内での 安全性が保証される。

パラメータ詳細

内部定数 説明
HERD_TIMEOUT 300.0 待機者が実行者の完了を待つ最大秒数
HERD_MAX_RETRIES 3 実行者が失敗またはタイムアウトした際のリトライ回数

エラーハンドリング

エッジケース

親: ARCH013

子: IMPL023, TST023

TST023 TST {h(g)} ✓ レビュー済

目的

Thundering Herd Protection は、同一キーへの並行リクエストが一斉に関数を実行する「Thundering Herd」問題を防ぐ。この保護が不完全だと、重い計算やAPI呼び出しが不要に多重実行され、リソース浪費やレートリミット超過を招く。並行性バグはテスト困難であるため、複数の観点から網羅的に検証する。

検証観点

references: tests/integration/core/test_thundering_herd.py

親: SPEC023

子:

IMPL023 IMPL {h(g)} ✓ レビュー済

実装概要

CacheManager クラス内で、Thundering Herd(キャッシュミス時に同一キーへの 大量アクセスが同時に発生する問題)を防止する直列化機構を実装。 _inflight 辞書で実行中のキーを管理し、最初の1スレッド/タスクだけが関数を実行し、 後続の呼び出しは herd_sync / herd_async コンテキスト内でその完了を待機する。

設計判断

コンテキストマネージャによる例外安全性

実行者 (Executor) が処理中に例外(KeyboardInterrupt や asyncio.CancelledError を含む)で 中断された場合でも、_inflight 状態が残留して後続タスクを永続的にブロックすることを防ぐため、 herd_sync / herd_async をコンテキストマネージャとして提供する。 クリーンアップ処理(notify_and_cleanup_inflight)は finally ブロック内で 自動的に実行される。

同期・非同期の統合的保護

_inflight 辞書の値として (threading.Event, list[asyncio.Future], list[result]) の タプルを保持し、スレッドベースの待機 (Event.wait) と asyncio ベースの待機 (Future) の 両方を単一の管理下で混在できるようにしている。

タイムアウトと再試行(自己回復)

関数の実行がハングアップした場合に待機側が永遠にブロックされるのを防ぐため、 HERD_TIMEOUT(300秒)と HERD_MAX_RETRIES(3回)を設定。 タイムアウト時には警告ログを出しつつ再試行し、上限を超えれば TimeoutError で フェイルファストする堅牢な設計とした。

実装メモ

references: src/beautyspot/cache.py

親: SPEC023

子: