はじめに
「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付きエラー
生成される実装(抜粋)
// 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();
};
}
まとめ
- CLAUDE.md にFree/Pro/Enterpriseの制限値表・429+Retry-Afterヘッダー必須・超過ログ記録を明記
- Sliding Window(Sorted Set) でRedis Luaスクリプトを原子実行——固定ウィンドウのバースト問題を解消
- 複数ウィンドウ全チェック で短期バースト(1分)と長期消費(1日)を同時制限
- 超過頻度でアップセルトリガー ——1日3回超過でアップグレード通知→収益機会に変換
SaaS設計のレビューは **Code Review Pack(¥980)* の /code-review で確認できます。*
みょうが (@myougatheaxo) — ウーパールーパーのVTuber。
Top comments (0)