@unikvs/rate-limit
トークンバケットで操作頻度を制限するミドルウェアです。
概要
RateLimit はトークンバケットで操作頻度を制限し、過剰な読み書きからストレージを保護します。Middleware
バーストを許容しつつ平均速度を抑えたいときに使います。制限超過時は待機か即時エラーを選べます。
nameはRateLimitです。isOpenは常にtrueです。- バケットはキーごとではなくミドルウェアインスタンスで共有され、すべての操作 (
set・get・stream・has・delete・copy・move・clear) で 1 トークンを消費します。 encode・decode・encodeStream・decodeStreamはデータを透過します。
インストールは次のコマンドで行います。
npm install @unikvs/rate-limitpnpm add @unikvs/rate-limityarn add @unikvs/rate-limitbun add @unikvs/rate-limitnub add @unikvs/rate-limitaube add @unikvs/rate-limit使い方
RateLimit は IMiddleware を実装しています。UniKvs には use() で渡します。
import { RateLimit } from "@unikvs/rate-limit";
コンストラクターの引数は次のとおりです。
tokensPerInterval?number
1 インターバルあたりに補充されるトークン数です。
number1intervalMs?number
トークンを補充する間隔 (ミリ秒) です。
number1000maxTokens?number
保持できるトークンの上限です。既定値は max(1, tokensPerInterval) です。1 未満を明示すると構築に失敗します。
numberburst?number
maxTokens の別名です。両方を指定した場合は maxTokens が優先されます。1 未満を明示すると構築に失敗します。
numberstrategy?RateLimitStrategy
レート制限を超えたときの挙動です。"wait" は補充まで待機し、"throw" は直ちにエラーを投げます。
RateLimitStrategy"wait"now?() => number
現在時刻 (epoch ミリ秒) を返す関数です。既定値は Date.now です。
() => numberDate.nowsleep?IRateLimitSleep
待機処理です。既定では setTimeout を使います。テストで時刻を制御するときに差し替えることができます。
IRateLimitSleep以下は、1 秒あたり 5 操作までに制限し、超過時は待機する例です。
import { Memory } from "@unikvs/memory";
import { RateLimit } from "@unikvs/rate-limit";
import * as unikvs from "unikvs";
const kvs = new unikvs.Builder<{ k1: unikvs.PlainValue<string> }>()
.use(new RateLimit({ tokensPerInterval: 5, intervalMs: 1000 }))
.persist(new Memory())
.build();
await kvs.open();
await kvs.set("k1", "hello");
console.log(await kvs.get("k1")); // "hello"
await kvs.close();
RateLimit
トークンバケットで操作頻度を制限するミドルウェアです。このミドルウェアは PassThrough を継承し、データ変換は行いません。
RateLimitOptions
RateLimit のコンストラクターが受け取るオプションです。トークン数と補充間隔、超過時の挙動を指定できます。
RateLimitStrategy
レート制限を超えたときの挙動です。"wait" ではトークンが補充されるまで待機し、"throw" では直ちにエラーを投げます。
IRateLimitSleep
待機処理の実装です。テストで時刻を制御するときに差し替えることができます。
データ型
| メソッド | 入力 | 出力 |
|---|---|---|
encode |
any |
any |
decode |
any |
any |
encodeStream |
any |
any |
decodeStream |
any |
any |
useは操作ごとに 1 トークンを消費し、データ内容には触れません。encode・decode・encodeStream・decodeStreamは入力をそのまま通し、変換を行いません。- トークンは経過時間に応じて補充され、上限を超えて増えることはありません。時計が戻ってもトークンは増えません。
maxTokensを省略した場合の容量はmax(1, tokensPerInterval)です。1 回の操作で 1 トークン消費するため、tokensPerIntervalが 1 未満でも容量は 1 になります。maxTokensまたはburstに 1 未満を明示すると、構築時にRateLimitInvalidOptionErrorを投げます。tokensPerIntervalには 1 未満の小数を指定できます。wait戦略では回復まで待機し、要求は先着順に実行されます。このミドルウェアは中断シグナルでは待機を打ち切ります。
エラー
このパッケージが投げるエラーは次のとおりです。
RateLimitExceededError
retryAfterMsnumber
再試行までに待つべき時間 (ミリ秒) です。
number意味
strategy が "throw" のときにトークンが不足したまま操作が呼ばれると、このエラーが投げられます。next は呼ばれず、操作は実行されません。
対処方法
meta.retryAfterMs の時間だけ待って再試行するか、strategy を "wait" にして自動待機を使ってください。頻発する場合は tokensPerInterval や maxTokens を見直してください。
例
以下は、RateLimitExceededError を捕捉する例です。
import { RateLimitExceededError } from "@unikvs/rate-limit";
try {
await kvs.set("k1", "hello");
} catch (ex) {
if (ex instanceof RateLimitExceededError) {
console.log(ex.meta); // { retryAfterMs: 500 }
} else {
throw ex;
}
}
RateLimitInvalidOptionError
意味
コンストラクターに不正なオプションが渡されると、このエラーが投げられます。正で有限の数値ではない値、1 未満の maxTokens と burst、"wait"・"throw" 以外の strategy、関数でない now・sleep が対象です。
対処方法
meta.name のオプションを確認し、正の有限数や正しい列挙値、関数を指定してください。maxTokens と burst の両指定では maxTokens が優先されます。
例
以下は、RateLimitInvalidOptionError を捕捉する例です。
import { RateLimit, RateLimitInvalidOptionError } from "@unikvs/rate-limit";
try {
const limiter = new RateLimit({ intervalMs: 0 });
} catch (ex) {
if (ex instanceof RateLimitInvalidOptionError) {
console.log(ex.meta); // { name: "intervalMs", actual: 0 }
} else {
throw ex;
}
}
中断シグナルによる AbortError はそのまま伝播し、このミドルウェアはトークンを消費しません。