---
title: Robust Rate Limiter Implementation
status: Proposed
date: 2025-11-21
context: Issue4 & Bug Fix
---

# 2. Smooth Rate Limiter (GCRA)

## Context / コンテキスト

`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 / 決定

アルゴリズムをトークンバケットから **GCRA (Generic Cell Rate Algorithm)**、あるいは "Leaky Bucket as a Meter" と呼ばれる方式に変更する。

* **理論的到達時刻 (TAT: Theoretical Arrival Time)** を管理する。
* タスクが来ると、そのコスト分だけ TAT を未来へ進める。
* `現在時刻 < TAT` ならば、その差分だけ待機（sleep）してから実行する。
* `現在時刻 >= TAT` （アイドル状態）ならば、TAT を現在時刻にリセットしてから計算する（過去の貯金は捨てる）。

## Consequences / 結果

* **メリット:**
    * **完全な定速運転:** アイドル後でもバーストせず、設定されたレート（TPM）を厳守する。
    * **デッドロックフリー:** どんなに大きなコストのタスクでも、必要な待機時間を計算して処理できる。
    * **即時起動:** 最初の1回のリクエストは待ち時間なしで実行され、2回目以降からペーシングが効く。
    * **Fail Fast:** 設定値を超える無茶なリクエストを早期発見できる。
* **デメリット:**
    * 実装が「トークンを消費する」という直感的なメタファーから少し離れる（コード内の説明で補足する）。

