← 全体レポートに戻る

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

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

対象アイテム

7

REQ

1

ARCH

1

SPEC

1

TST

1

IMPL

1

ADR

2

レビュー済

7/7

Suspect

0

グループ

LIMIT
アイテム: REQ008 ARCH008 SPEC018 TST018 IMPL018 ADR003 ADR031

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

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

グループREQARCHSPECTSTIMPLADR
LIMITREQ008
関数の実行頻度をトークンバケット方式で制限し、外部APIの呼び出しレートを制御できること。同期・非同期の両方に対応すること。
ARCH008
## コンポーネント構成 ```mermaid graph TD A["@spot.consume"] -->|宣言的適用| B[LimiterProto...
SPEC018
## インターフェース ```python class TokenBucket(LimiterProtocol): def consume(self,...
TST018
## 目的 `TokenBucket` はAPI呼び出しやリソースアクセスのレート制限を実現する。レートリミッターの不具合はAPIの過負荷(制限が甘い場合)や...
IMPL018
## 実装概要 `LimiterProtocol` を実装する `TokenBucket` クラス。 GCRA (Generic Cell Rate Algo...
ADR003
# Smooth Rate Limiter (GCRA) ## Context and Problem Statement / コンテキスト `beauty...
LIMITREQ008
関数の実行頻度をトークンバケット方式で制限し、外部APIの呼び出しレートを制御できること。同期・非同期の両方に対応すること。
ARCH008
## コンポーネント構成 ```mermaid graph TD A["@spot.consume"] -->|宣言的適用| B[LimiterProto...
SPEC018
## インターフェース ```python class TokenBucket(LimiterProtocol): def consume(self,...
TST018
## 目的 `TokenBucket` はAPI呼び出しやリソースアクセスのレート制限を実現する。レートリミッターの不具合はAPIの過負荷(制限が甘い場合)や...
IMPL018
## 実装概要 `LimiterProtocol` を実装する `TokenBucket` クラス。 GCRA (Generic Cell Rate Algo...
ADR031
# Limiter Dependency Injection ## Context and Problem Statement / コンテキスト 以前、`c...

カバレッジ(局所)

リンク方向カバー数カバー率未カバー
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 1 / 1 100.0%

アイテム詳細

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

関数の実行頻度をトークンバケット方式で制限し、外部APIの呼び出しレートを制御できること。同期・非同期の両方に対応すること。

親:

子: ADR003, ADR031, ARCH008

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

コンポーネント構成

graph TD
  A["@spot.consume"] -->|宣言的適用| B[LimiterProtocol]
  C[core.Spot] -->|DI| B
  D[TokenBucket] --> B
  D -->|アルゴリズム| E[GCRA]
  D -->|時刻同期| F[monotonic clock]

コンポーネント責務

コンポーネント 責務 インターフェース
TokenBucket GCRAアルゴリズムによるスムースなトラフィック制御 consume(), consume_async()
@spot.consume 関数実行前のトークン消費を透過的に行うデコレータ cost (int or callable)
LimiterProtocol レートリミッターの差し替えを可能にするインターフェース LimiterProtocol

データフロー

sequenceDiagram
  participant User as ユーザーコード
  participant Dec as @spot.consume
  participant Bucket as TokenBucket

  User->>Dec: call func()
  Dec->>Bucket: consume(cost)
  Bucket->>Bucket: 次回実行許可時刻の計算 (GCRA)
  alt 許可
      Bucket-->>Dec: OK
      Dec->>User: execute func()
  else 拒否 (Over rate)
      Bucket-->>Dec: raise ValueError/RateLimitError
  end

技術選定

技術領域 選定 理由
アルゴリズム GCRA (Generic Cell Rate Algorithm) 固定ウィンドウと違い「スムースな」流量制御が可能
待機制御 asyncio.sleep / time.sleep 同期・非同期の両方のコンテキストで正確なスロットリングを実現
コスト計算 ダイナミックコスト 引数に応じて消費トークン数を動的に変更可能

非機能要件方針

親: REQ008

子: SPEC018

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

インターフェース

class TokenBucket(LimiterProtocol):
    def consume(self, cost: float = 1.0) -> None: ...
    async def consume_async(self, cost: float = 1.0) -> None: ...

振る舞い

GCRAアルゴリズム

同期 vs 非同期

パラメータ詳細

設定 デフォルト 説明
tokens_per_minute 必須 1分間に許可する平均リクエスト数(コスト合計)
max_burst 1.0 許容されるバースト数(トークン単位)

エラーハンドリング

エッジケース

親: ARCH008

子: IMPL018, TST018

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

目的

TokenBucket はAPI呼び出しやリソースアクセスのレート制限を実現する。レートリミッターの不具合はAPIの過負荷(制限が甘い場合)やスループットの不必要な低下(制限が厳しすぎる場合)を招く。GCRA アルゴリズムのスムーズなレート制御を検証する。

検証観点

references: tests/unit/test_limiter.py

親: SPEC018

子:

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

実装概要

LimiterProtocol を実装する TokenBucket クラス。 GCRA (Generic Cell Rate Algorithm) に基づき、スレッドセーフおよび 非同期対応のスムーズなレートリミッタを提供する。 内部状態として Theoretical Arrival Time (TAT) を保持し、 consume() で同期的スリープ、consume_async() で非同期的スリープを行う。

設計判断

GCRAアルゴリズムの採用

伝統的なトークンバケットとは異なり、長時間アイドル後に 一気にバーストを許容しない「Strict Pacing」を実現できる。 TATの更新と現在時刻の比較だけで待機時間を計算できるため、 メモリ効率と計算効率に優れる。

モノトニッククロックの利用

時刻の取得に time.monotonic() を使用し、システム時刻の変更(NTP同期など)の 影響を受けない堅牢な設計としている。

実装メモ

references: src/beautyspot/limiter.py

親: SPEC018

子:

ADR003 ADR {h(g)} ✓ レビュー済

Smooth Rate Limiter (GCRA)

Context and Problem Statement / コンテキスト

beautyspot のレート制限機能 (limiter) において、以下の課題があった。

  1. Idle Burst: トークンバケット方式では、アイドル中にトークンが溜まり、再開直後にバースト(集中アクセス)が発生してしまう。これを防ぐために容量(capacity)を小さくすると、今度は巨大なコストを持つタスクが実行できなくなる(デッドロック)問題が発生する。
  2. Start Dash Prevention: プロセス起動直後に複数のタスクが同時に走るのを防ぎたいが、最初の1回目まで待たされるのは避けたい。
  3. Clock Dependency: システム時刻の変更に堅牢である必要がある。
  4. Max Cost Guard: tokens_per_minute を単発タスクのコスト上限とする。
    • これを超えるコストが consume() に渡された場合、即座に ValueError を送出する。
    • 理由: APIの物理的なレート制限を超えるリクエストは、待機したところで成功する見込みが薄く、早期に設定ミスや入力ミスをユーザーに通知すべきであるため。

Decision Drivers / 要求

Considered Options / 検討

Decision Outcome / 決定

Chosen option: Option 2.

アルゴリズムをトークンバケットから GCRA (Generic Cell Rate Algorithm) に変更する。

Consequences / 決定

親: REQ008

子:

ADR031 ADR {h(g)} ✓ レビュー済

Limiter Dependency Injection

Context and Problem Statement / コンテキスト

以前、core.pySpot クラスは TokenBucket 実装と密結合していました。Spottpm (tokens per minute) 整数引数を受け取り、内部で TokenBucket をインスタンス化していました。

この設計にはいくつかの制限がありました: 1. Testing: ユニットテストが TokenBucket 内の本物の time.sleep 呼び出しに依存するため、実行が遅くなっていました。 2. Extensibility: ユーザーがカスタムレートリミッター(例:Redisベースの分散リミッター)や異なるアルゴリズムを提供できませんでした。 3. Separation of Concerns: core.Spot がリミッターのライフサイクルと設定を管理しており、単一責任の原則に違反していました。

Decision Drivers / 要求

Considered Options / 検討

Decision Outcome / 決定

Chosen option: Option 2.

レートリミッターを core.Spot から切り離し、依存性注入 (DI) を使用します。

  1. Protocol Definition: beautyspot.limiterconsume(cost: int)consume_async(cost: int) メソッドを規定する LimiterProtocol を定義します。
  2. Explicit Inheritance: デフォルトの TokenBucket 実装は、型安全性と明確さのために LimiterProtocol を明示的に継承します。
  3. Injection: core.Spot.__init__ を、tpm の代わりに limiter: LimiterProtocol インスタンスを受け取るように変更します。
  4. Factory Responsibility: __init__.pySpot ファクトリ関数が、カスタムリミッターが提供されない場合のデフォルトの TokenBucket 作成を担当します。

Consequences / 決定

親: REQ008

子: