Repository navigation
fix(executor): upstream_empty 不冷却账号 / do not cool accounts down for upstream_empty - #288
Merged
caigee-cmd merged 1 commit intoOct 9, 2026
Conversation
A heartbeat-only upstream stream (upstream_empty, introduced as a pre-relay guard in caigee-cmd#286) says nothing about the account's health: credential, quota, and rate-limit state are all fine, and the same account may succeed on the very next request. Classifying it as a generic unavailable failure applies the default 15s cooldown with exponential backoff on repeats, so whenever the upstream empty-stream rate spikes the pool starves: every account ends up cooled down and requests queue behind unrelated timeouts. Observed live while the upstream empty-stream rate was around 40-60%. Special-case Code=upstream_empty in ClassifyError: keep Failover=true (another account serves the turn) but set Cooldown=0 so the account stays in the rotation. Generic unavailable errors keep their cooldown.
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.
中文
问题现象
#286 合入后的实测中发现:当上游空流率高企时(一次实验中 6 发有 4 发空流,约 40-60%),qoder 账号出现频繁冷却,账号池被饿死,请求在长时间排队后才成功。
根因
upstream_empty(#286 引入的转发前守卫)目前按普通unavailable错误分类,走Classify的 default 分支:ParseRetryAfterHint(body, retryAfter, 15*time.Second))Pool.MarkClassified对同类错误的重复失败还会指数退避(15s → 更长)但空流只说明 Qoder 网关本次没有产出内容,账号本身完全健康——凭据、配额、限流状态都正常,同一账号下一次请求就可能成功。把"上游瞬时故障"按"账号故障"冷却,在空流率高时会把整个账号池冻结。
修复
internal/executor/error.go的ClassifyError中对Code == "upstream_empty"特例处理:Failover = true—— 本次请求仍然换一个账号服务(保留 fix(qoder): 转发前拒绝上游空流 / reject empty upstream streams before relaying #286 的收益)Cooldown = 0—— 账号立即回到轮询池,不被惩罚其他
unavailable错误(连接被重置等真实账号层故障)保持原有 15 秒冷却不变。Pool.MarkClassified中Cooldown=0天然不设置DownUntil,无需改池逻辑。测试
新增
TestClassifyErrorUpstreamEmptyNoCooldown:upstream_empty→Failover=true、Cooldown=0、RetryAfter=0go test ./...、go test -race、go vet ./...、gofmt全部通过。English
Problem
Live testing after #286: when the upstream empty-stream rate spiked (4/6 requests in one sample, ~40-60%), qoder accounts were cooled down repeatedly and the pool starved — requests queued behind long cooldowns even though the accounts themselves were healthy.
Root cause
upstream_empty(the pre-relay guard from #286) is classified as a genericunavailableerror, which applies the default 15s cooldown plus exponential backoff on repeat failures. But a heartbeat-only upstream stream says nothing about the account's health: credentials, quota, and rate-limit state are all fine, and the same account may succeed on the very next request. Cooling accounts for a transient upstream fault freezes the whole pool when the empty-stream rate is high.Fix
Special-case
Code == "upstream_empty"inClassifyError: keepFailover = true(another account serves the turn) but setCooldown = 0so the account stays in the rotation. Genericunavailableerrors keep their 15s cooldown.Pool.MarkClassifiedalready treatsCooldown=0as noDownUntil, so no pool changes needed.Tests
New
TestClassifyErrorUpstreamEmptyNoCooldowncovers both the exemption and a control case (generic unavailable keeps its cooldown).go test ./...,-race,go vet,gofmtall pass.