DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Claude Codeでプランティア別レート制限を設計する:Free/Pro/Enterprise・Redis Sliding Window

はじめに

「Free/Pro/Enterpriseプランで呼び出し上限が違う」——プランティアに応じたレート制限をSlidingWindowアルゴリズムとRedisで実装し、429+Retry-Afterを返す設計をClaude Codeに生成させる。


CLAUDE.mdに設計ルールを書く

## プランティア別レート制限設計ルール

| プラン | 1分 | 1時間 | 1日 |
|--------|-----|-------|-----|
| free   | 20  | 100   | 500 |
| pro    | 100 | 1,000 | 10,000 |
| enterprise | 1,000 | 50,000 | 無制限 |

- Sliding Window(Sorted Set): 固定ウィンドウより均一な制限
- レスポンスヘッダー: X-RateLimit-Limit, Remaining, Reset, Retry-After
- 超過時: 429 + アップグレードURL付きエラー
Enter fullscreen mode Exit fullscreen mode

生成される実装(抜粋)

// Lua スクリプトで原子的なSliding Window
const luaScript = `
  local key = KEYS[1]
  redis.call('ZREMRANGEBYSCORE', key, '-inf', ARGV[2]) -- 期限切れ削除
  local count = redis.call('ZCARD', key)
  if count < tonumber(ARGV[3]) then
    redis.call('ZADD', key, ARGV[1], ARGV[1]..'-'..math.random(1000000))
    redis.call('PEXPIRE', key, ARGV[4])
    return {1, tonumber(ARGV[3]) - count - 1} -- {allowed, remaining}
  else
    local oldest = redis.call('ZRANGE', key, 0, 0, 'WITHSCORES')
    return {0, tonumber(oldest[2])}
  end
`;

// Expressミドルウェア
export function rateLimitByTier(endpoint?: string) {
  return async (req, res, next) => {
    const tier: PlanTier = req.user?.subscription?.plan ?? 'free';
    const { allowed, mostRestrictive } = await rateLimiter.checkAll(req.userId, tier, endpoint);

    res.set({
      'X-RateLimit-Limit': mostRestrictive?.limit.toString(),
      'X-RateLimit-Remaining': mostRestrictive?.remaining.toString(),
      'X-RateLimit-Reset': Math.floor(mostRestrictive?.resetAt.getTime() / 1000).toString(),
    });

    if (!allowed) {
      const retryAfterSec = Math.ceil(mostRestrictive?.retryAfterMs / 1000);
      res.set('Retry-After', retryAfterSec.toString());
      return res.status(429).json({
        error: 'Too Many Requests',
        retry_after: retryAfterSec,
        upgrade_url: tier !== 'enterprise' ? 'https://example.com/pricing' : undefined,
      });
    }
    next();
  };
}
Enter fullscreen mode Exit fullscreen mode

まとめ

  1. CLAUDE.md にFree/Pro/Enterpriseの制限値表・429+Retry-Afterヘッダー必須・超過ログ記録を明記
  2. Sliding Window(Sorted Set) でRedis Luaスクリプトを原子実行——固定ウィンドウのバースト問題を解消
  3. 複数ウィンドウ全チェック で短期バースト(1分)と長期消費(1日)を同時制限
  4. 超過頻度でアップセルトリガー ——1日3回超過でアップグレード通知→収益機会に変換

SaaS設計のレビューは **Code Review Pack(¥980)* の /code-review で確認できます。*

prompt-works.jp

みょうが (@myougatheaxo) — ウーパールーパーのVTuber。

Top comments (0)