Migration 0041_rate_limit adds rate_limit_bucket. PgTokenBucket (@umt/db) refills and takes tokens under SELECT ... FOR UPDATE on the DB clock and polls with jitter within a wait budget; penalize() carries Retry-After. Site adapters get createSharedSiteRateLimiter (key site:<id>), shared by api / worker / worker:agent, with an in-process serial queue in front and a local fallback when the bucket is unavailable. Co-Authored-By: Claude Opus 5.5 <[email protected]>
22 lines
881 B
TypeScript
22 lines
881 B
TypeScript
// 子进程:对同一 key 连续扣 N 个令牌,每次成功输出一行时间戳(毫秒)。由 rate-limit.test.ts 以 Deno 子进程启动。
|
|
import { Kysely, PostgresDialect } from "kysely";
|
|
import pg from "pg";
|
|
import { PgTokenBucket } from "../../src/rate-limit.ts";
|
|
|
|
const [url, schema, key, n, capacity, rate] = Deno.args as [string, string, string, string, string, string];
|
|
const db = new Kysely<unknown>({
|
|
dialect: new PostgresDialect({
|
|
pool: new pg.Pool({ connectionString: url, max: 1, options: `-c search_path=${schema},public` }),
|
|
}),
|
|
});
|
|
const bucket = new PgTokenBucket(db);
|
|
try {
|
|
for (let i = 0; i < Number(n); i++) {
|
|
const r = await bucket.acquire(key, 1, { capacity: Number(capacity), refillPerSec: Number(rate), waitMs: 30_000 });
|
|
if (!r.ok) throw new Error("等待超时");
|
|
console.log(Date.now());
|
|
}
|
|
} finally {
|
|
await db.destroy();
|
|
}
|