fix(rate-limit): normalize queue refresh settings (#5499)

Co-authored-by: KooshaPari <koosha@example.com>
This commit is contained in:
KooshaPari
2026-06-29 18:03:31 -07:00
committed by GitHub
parent 6b9c7d14df
commit c8814edd47
2 changed files with 44 additions and 12 deletions

View File

@@ -140,9 +140,7 @@ function resolveMinTime(override: number | undefined | null): number {
// Resolve a maxConcurrent override. 0 or missing means "effectively infinite".
function resolveMaxConcurrent(override: number | undefined | null): number {
return typeof override === "number" && override > 0
? override
: EFFECTIVELY_INFINITE_CONCURRENCY;
return typeof override === "number" && override > 0 ? override : EFFECTIVELY_INFINITE_CONCURRENCY;
}
function buildLimiterDefaults() {
@@ -159,14 +157,9 @@ function buildLimiterDefaults() {
}
function updateAllLimiterSettings() {
const defaults = buildLimiterDefaults();
for (const limiter of limiters.values()) {
limiter.updateSettings({
maxConcurrent: currentRequestQueueSettings.concurrentRequests,
minTime: currentRequestQueueSettings.minTimeBetweenRequestsMs,
reservoir: currentRequestQueueSettings.requestsPerMinute,
reservoirRefreshAmount: currentRequestQueueSettings.requestsPerMinute,
reservoirRefreshInterval: 60 * 1000,
});
limiter.updateSettings(defaults);
}
}
@@ -234,7 +227,9 @@ function watchdogTick() {
limiters.delete(key);
lastDispatchAt.delete(key);
limiterLastUsed.delete(key);
logRateLimit(`🧹 [RATE-LIMIT] Evicting idle limiter: ${key} (inactive for ${Math.round((now - lastUsed) / 1000)}s)`);
logRateLimit(
`🧹 [RATE-LIMIT] Evicting idle limiter: ${key} (inactive for ${Math.round((now - lastUsed) / 1000)}s)`
);
trackAsyncOperation(limiter.disconnect());
}
}
@@ -535,7 +530,12 @@ export async function withRateLimit(provider, connectionId, model, fn, signal =
// Proactive sliding-window fallback for header-less providers with a declared cap
// (Fase 8.2). No-op unless PROVIDER_DEFAULT_RATE_LIMITS has an entry for `provider`.
await awaitProviderDefaultSlot(provider, connectionId, signal, currentRequestQueueSettings.maxWaitMs);
await awaitProviderDefaultSlot(
provider,
connectionId,
signal,
currentRequestQueueSettings.maxWaitMs
);
const limiter = getLimiter(provider, connectionId, model);
const maxWaitMs = currentRequestQueueSettings.maxWaitMs;

View File

@@ -202,3 +202,35 @@ test("after 429 teardown, next withRateLimit must get a fresh limiter and succee
);
assert.equal(result, "post-429", "post-429 request must return its value");
});
test("request queue refresh treats zero limits as unbounded for existing limiters", async () => {
await flushBackgroundWork();
const provider = "openai";
const connectionId = "lifecycle-test-conn-d";
rateLimitManager.enableRateLimitProtection(connectionId);
assert.equal(
await rateLimitManager.withRateLimit(
provider,
connectionId,
null,
async () => "before-refresh"
),
"before-refresh"
);
await rateLimitManager.applyRequestQueueSettings({
enabled: true,
autoEnableApiKeyProviders: false,
maxWaitMs: 100,
requestsPerMinute: 0,
concurrentRequests: 0,
minTimeBetweenRequestsMs: 0,
});
assert.equal(
await rateLimitManager.withRateLimit(provider, connectionId, null, async () => "after-refresh"),
"after-refresh"
);
});