mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-17 04:12:17 +03:00
Behind `PROXY_SKIP_RECENTLY_FAILED` (from #13578): a provider 429 received through a pool member sets that member aside and a 2xx clears it, for opencode providers.
Maintainer rework before merge (kept the idea, no default behavior change):
- `noteProxyOutcome` ran inside the fire-and-forget `safeLogEvents` after awaited dynamic imports, so a concurrent request could still pick the member; it now runs first, synchronously, before any `await`.
- The duplicate `177_proxy_logs_upstream_status.sql` the stack still carried alongside the renamed 179 was removed; the regression test the PR body named exists as `pool-ip-quota-429-path.test.ts`.
Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 c0f92ec: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests.
Thanks @maxmad64bis!
101 lines
4.2 KiB
TypeScript
101 lines
4.2 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
// With PROXY_SKIP_RECENTLY_FAILED on, a pool member is set aside only on a refusal the
|
|
// provider really returned through it, and only for a provider inside the refusal scope.
|
|
// Locally generated failures carry no upstream outcome and never set a member aside. With
|
|
// the flag off (the default) nothing is ever written.
|
|
|
|
const memory = await import("../../open-sse/utils/proxyRefusalMemory.ts");
|
|
const { noteProxyOutcome } = await import("../../src/sse/handlers/proxyOutcomeMemory.ts");
|
|
const { egressBucketedLockProviders } = await import("../../open-sse/config/providerErrorRules.ts");
|
|
|
|
const PROXY = { type: "http", host: "10.4.0.1", port: 8080 };
|
|
const KEY = memory.proxyEgressKey(PROXY);
|
|
|
|
test.beforeEach(() => {
|
|
memory.__resetProxyRefusalMemoryForTesting();
|
|
process.env.PROXY_SKIP_RECENTLY_FAILED = "true";
|
|
});
|
|
|
|
test.after(() => {
|
|
delete process.env.PROXY_SKIP_RECENTLY_FAILED;
|
|
});
|
|
|
|
test("a received refusal from an in-scope provider sets the member aside", () => {
|
|
for (const provider of [...egressBucketedLockProviders(), "OpenCode"]) {
|
|
memory.__resetProxyRefusalMemoryForTesting();
|
|
noteProxyOutcome(provider, { proxy: PROXY, upstreamStatus: 429 });
|
|
assert.equal(memory.isProxyAvoided(KEY), true, provider);
|
|
}
|
|
});
|
|
|
|
test("a refusal from a provider outside the scope writes nothing", () => {
|
|
for (const provider of ["opencode-zen", "openai", null]) {
|
|
noteProxyOutcome(provider, { proxy: PROXY, upstreamStatus: 429 });
|
|
}
|
|
assert.equal(memory.__proxyRefusalMemorySizeForTesting(), 0);
|
|
});
|
|
|
|
test("no received status, no proxy or no proxyInfo writes nothing", () => {
|
|
noteProxyOutcome("opencode", { proxy: PROXY });
|
|
noteProxyOutcome("opencode", { proxy: PROXY, upstreamStatus: null });
|
|
noteProxyOutcome("opencode", { proxy: null, upstreamStatus: 429 });
|
|
noteProxyOutcome("opencode", null);
|
|
noteProxyOutcome("opencode", undefined);
|
|
assert.equal(memory.__proxyRefusalMemorySizeForTesting(), 0);
|
|
});
|
|
|
|
test("a success from an in-scope provider clears the member, other outcomes leave it", () => {
|
|
noteProxyOutcome("opencode", { proxy: PROXY, upstreamStatus: 429 });
|
|
noteProxyOutcome("opencode", { proxy: PROXY, upstreamStatus: 403 });
|
|
noteProxyOutcome("opencode", { proxy: PROXY, upstreamStatus: 500 });
|
|
assert.equal(memory.isProxyAvoided(KEY), true);
|
|
noteProxyOutcome("opencode-go", { proxy: PROXY, upstreamStatus: 200 });
|
|
assert.equal(memory.isProxyAvoided(KEY), false);
|
|
});
|
|
|
|
test("a success from another provider only ends an unreachable period, not a refusal one", () => {
|
|
noteProxyOutcome("opencode", { proxy: PROXY, upstreamStatus: 429 });
|
|
noteProxyOutcome("openai", { proxy: PROXY, upstreamStatus: 200 });
|
|
assert.equal(
|
|
memory.isProxyAvoided(KEY),
|
|
true,
|
|
"a shared pool must not clear another scope's refusal"
|
|
);
|
|
|
|
memory.__resetProxyRefusalMemoryForTesting();
|
|
memory.noteProxyRefusal(KEY, "proxy_unreachable");
|
|
noteProxyOutcome("openai", { proxy: PROXY, upstreamStatus: 200 });
|
|
assert.equal(memory.isProxyAvoided(KEY), false);
|
|
});
|
|
|
|
test("an edge relay is ignored: its status is the relay's", () => {
|
|
noteProxyOutcome("opencode", {
|
|
proxy: { type: "vercel", host: "relay.example.vercel.app", port: 443 },
|
|
upstreamStatus: 429,
|
|
});
|
|
noteProxyOutcome("opencode", {
|
|
proxy: { type: "Vercel", host: "relay.example.vercel.app", port: 443 },
|
|
upstreamStatus: 429,
|
|
});
|
|
assert.equal(memory.__proxyRefusalMemorySizeForTesting(), 0);
|
|
});
|
|
|
|
test("a second note while the proxy is already set aside changes nothing", () => {
|
|
const start = Date.now();
|
|
const periodMs = memory.noteProxyRefusal(KEY, "ip_quota_429", start) ?? 0;
|
|
noteProxyOutcome("opencode", { proxy: PROXY, upstreamStatus: 429 });
|
|
assert.equal(memory.__proxyRefusalMemorySizeForTesting(), 1);
|
|
assert.equal(memory.isProxyAvoided(KEY, start + periodMs - 1000), true);
|
|
assert.equal(memory.isProxyAvoided(KEY, start + periodMs + 1000), false);
|
|
});
|
|
|
|
test("with the flag at its default (off) a received refusal writes nothing", () => {
|
|
delete process.env.PROXY_SKIP_RECENTLY_FAILED;
|
|
for (const provider of egressBucketedLockProviders()) {
|
|
noteProxyOutcome(provider, { proxy: PROXY, upstreamStatus: 429 });
|
|
}
|
|
assert.equal(memory.__proxyRefusalMemorySizeForTesting(), 0);
|
|
});
|