active: true
derived: false
gherkin: |
  Feature: TokenBucket (GCRA) rate limiting
    Scenario: Enforcing rate limits on requests
      Given a TokenBucket limiter configured with tokens per minute
      When consume is called repeatedly
      Then it allows bursts up to the max limit and throttles or rejects subsequent requests based on TAT
groups:
- LIMIT
header: |
  TokenBucket (GCRA)
level: 2.18
links:
- ARCH008: 3P_GwS0JFM40QovY318yJfHonSHWqQiSUXfnIrPXI7w=
normative: true
ref: ''
reviewed: Rk1IozVD5Sbu6p9PQuXqfsh55lfSlYxKi-gO57G36TQ=
text: |
  ## インターフェース

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

  ## 振る舞い

  ### GCRAアルゴリズム
  - 各リクエストに対して「次回実行可能な理論時刻 (TAT: Theoretical Arrival Time)」を計算・更新する。
  - 現在時刻が `TAT - 許容バースト` 以前の場合、リクエストを拒否または待機させる。

  ### 同期 vs 非同期
  - `consume`: 待機が必要な場合は `time.sleep()` でブロック。
  - `consume_async`: 待機が必要な場合は `asyncio.sleep()` で非占有待機。

  ## パラメータ詳細

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

  ## エラーハンドリング

  - **即時拒否モード**: 待機を許容しない設定でレート超過した場合、`RateLimitError` (ValueErrorのサブクラス) を送出する。

  ## エッジケース

  - **高コストリクエスト**: `max_burst` を超えるコストを持つ単一リクエストは、常に拒否される。
  - **アイドル時間**: 長時間リクエストがない場合も、`TAT` は現在時刻より過去に一定以上離れないよう補正される（過度なバーストの防止）。

  ## 設計判断

  外部APIのレート制限（例: 1分間に100リクエスト）を正確に守るため。GCRA（Generic Cell Rate Algorithm）により、バーストを許容しつつ、滑らかで厳密な流量制御を実現する。
