feat(resilience): stop retrying sibling connections after an IP-bucketed 429 - #10920
Merged
diegosouzapw merged 1 commit intoAug 21, 2026
Conversation
maxmad64bis
force-pushed
the
feat/10880-egress-ip-lock
branch
from
August 20, 2026 23:38
01dadac to
cb04df0
Compare
maxmad64bis
marked this pull request as draft
August 20, 2026 23:55
maxmad64bis
force-pushed
the
feat/10880-egress-ip-lock
branch
2 times, most recently
from
August 21, 2026 00:13
c0708c9 to
1363209
Compare
maxmad64bis
marked this pull request as ready for review
August 21, 2026 00:14
…ted 429 The opencode free tier is IP-based, not account-based (diegosouzapw#9611). When connections share an egress IP they share one bucket, and today's rotation still tries each of them after the first 429 — one guaranteed-failed call per remaining connection, against an upstream that already refused. Cool every allowlisted connection sharing the failing one's last known egress IP so rotation skips them. No identity is created, no IP rotated, no header synthesized: this only removes calls. The lock stays inside the provider family, fires only on a 429, never turns terminal, and does nothing when the egress IP can't be resolved. It runs on the combo path too, like the connection-scoped agentrouter branch above it. For the allowlisted family a 429 now cools the connection where it used to lock a single model.
maxmad64bis
force-pushed
the
feat/10880-egress-ip-lock
branch
from
August 21, 2026 00:18
1363209 to
5118d35
Compare
diegosouzapw
merged commit Aug 21, 2026
65dcb1d
into
diegosouzapw:release/v3.8.50
7 of 16 checks passed
backryun
added a commit
to backryun/OmniRoute
that referenced
this pull request
Aug 21, 2026
…call sites Three getProviderConnectionById sites landed on release/v3.8.50 without updating the frozen inventory: - src/app/api/providers/[id]/refresh-token/route.ts (diegosouzapw#10910 TLS factory) - src/lib/kimi/tokenRefresh.ts (diegosouzapw#10944 kimi web-token lifecycle) - src/app/api/usage/utilization/route.ts (4226382, this PR's fix) and diegosouzapw#10920 added a fourth sibling re-read inside src/sse/services/auth.ts (3 → 4). All are classified B (connection-query, lease-checked) per the existing classification table. 3/3 green.
backryun
added a commit
to backryun/OmniRoute
that referenced
this pull request
Aug 21, 2026
…call sites Three getProviderConnectionById sites landed on release/v3.8.50 without updating the frozen inventory: - src/app/api/providers/[id]/refresh-token/route.ts (diegosouzapw#10910 TLS factory) - src/lib/kimi/tokenRefresh.ts (diegosouzapw#10944 kimi web-token lifecycle) - src/app/api/usage/utilization/route.ts (4226382, this PR's fix) and diegosouzapw#10920 added a fourth sibling re-read inside src/sse/services/auth.ts (3 → 4). All are classified B (connection-query, lease-checked) per the existing classification table. 3/3 green.
5 tasks
This was referenced Sep 13, 2026
5 tasks
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…ted 429 (diegosouzapw#10920) Validado no worktree combinado: typecheck:core, changelog-integrity, complexity, cognitive-complexity, file-size, lint e testes focados (egress-ip-lock-10880, egress-lock-allowlist-10880, proxy-logs-egress-lookup-10880) todos verdes. Otimização de resiliência bem fundamentada (cooldown de conexões compartilhando IP de egress após 429 do allowlist). CI vermelho é o base-red já rastreado em diegosouzapw#9985. Obrigado!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This removes upstream calls, it doesn't add capacity. Nothing here creates an identity, rotates an IP, or synthesizes a header — it just stops hammering a bucket that already said no.
You established in #9611 that the opencode free tier is IP-based, not account-based, and that real rotation needs a distinct proxy IP per account. Taking you at your word: when connections do share an IP, the current rotation still tries each of them after the first 429, so one exhausted bucket costs one guaranteed-failed call per remaining connection. Those calls hit an upstream that has already refused.
So on a 429 from an allowlisted provider, cool every connection sharing the failing one's last known egress IP. Rotation skips them instead of trying them.
Unlike #10357, nothing here depends on how the upstream responds — the branch runs after a 429 is already in hand and only writes local cooldown state, so there's no upstream round-trip to validate.
One behavior change to weigh: opencode is
passthroughModels, so a 429 used to lock a single model and now cools the connection, sibling or not. That's what the opencode rule table already declares (scope: "connection") and never got to apply. Removing the provider from the allowlist reverts it. Limits are indocs/architecture/RESILIENCE_GUIDE.md§7.Related Issues
Validation
npm run lintThe "one call instead of N" claim is easy to fake, so the test walks the whole pool with no early exit and skips only what credential selection skips. The same rotation on a provider that didn't opt in gets 3 — that gap is the measurement, not the
1. A third case runs it withisCombo: trueand also gets 1.Cost, on a real-traffic DB copy: 4708 rows in the 24h window out of 104624 in
proxy_logs, sibling query ~14 ms. Two window-bounded scans at 429 frequency, in a try/catch — a DB failure never reaches the caller.Inherited from the base:
check:mutation-test-coverage(entries owned by the base and other branches; the 3 files here are registered, delta 0) andcheck:docs-all, which stops atcheck:env-doc-syncoverBOT_TOKEN/BOT_URLfromscripts/ad-hoc/mesh-*.mjs. The two docs gates behind it were run alone and pass.Tests Added Or Updated
tests/unit/egress-lock-allowlist-10880.test.ts— the allowlist predicate.tests/unit/proxy-logs-egress-lookup-10880.test.ts— the egress-IP lookup and its window.tests/unit/egress-ip-lock-10880.test.ts— the branch: both accepted reasons, never terminal, longer sibling cooldowns never shortened, terminal siblings untouched, unresolvable IP,disableCoolingopt-out, the 1/3/combo rotation trio, cross-provider isolation, 401/403 still landing on per-model lockout.Coverage Notes
auth.ts,providerErrorRules.tsandproxyLogs.tsare each covered by the matching file above. No touched file loses coverage; mutation-gate delta is 0.Reviewer Notes
disableCoolingopts out.proxy_logswhile the egress-IP cache lives 5 minutes, so it's history: a connection that rotated IPs inside the window can be missed, or cooled after moving off the exhausted IP. §7 says so.rate_limit_exceededis accepted next toquota_exhaustedbecause the opencode rules can't match on this path:markAccountUnavailablepassescheckFallbackErrorneither headers norstructuredError, and opencode isn't inFULL_TEXT_RULE_PROVIDERS. Threading the headers through is the real fix — say the word and I'll do that one first instead.