Repository navigation
v2.24.6
🐛 Bug Fixes
internal/lb — Smart Probe 7 项修复
1. WithTimeout 未同步 prober 客户端
调用 WithTimeout 仅更新了 hc.client 和 hc.timeout,但 hc.prober 持有独立的 http.Client,导致 health_check_timeout 配置对智能探活(Discover 阶段)的 HTTP 请求完全无效。
修复:WithTimeout 现同时重建 hc.prober = NewProber(d, h.logger),三者超时严格一致。
2. 空白 API Key 被当作有效凭证
injectCredential 原先检查 cred.APIKey == "",纯空格或制表符组成的 key 会通过检查并注入无效的 Authorization header,向上游发出带噪声认证头的请求。
修复:先 strings.TrimSpace() 再判空,空白字符串视为"无凭证",不注入任何认证头。
3. buildProbeURL 未过滤查询参数和 Fragment
当目标地址(addr)包含 ?query 或 #fragment 时,探活 URL 会被拼接成无效路径(如 http://host/v1?key=val/models)。
修复:使用 url.Parse 提取路径,并用 strings.IndexAny(addrBase, "?#") 在构建 addrBase 前截断。
4. Discover 端点超时 vs 连接拒绝未区分
原 Discover 将所有 definitivelyUnhealthy() 错误均视为连接拒绝(unreachable=true),但 HTTP 请求超时也会触发此分支,导致临时超时的目标被错误地标记为不可达。
修复:新增 isEndpointTimeout() helper,区分两类失败:
- 端点超时(
*url.Error.Timeout()、context.DeadlineExceeded)→continue尝试下一路径 - 硬连接失败(拒绝连接/DNS 错误)→ 返回
unreachable=true
5. 所有路径超时时错误标记 unreachable
若 Discover 期间所有探活路径均超时(无 HTTP 响应),或上层 context 预算耗尽,原实现语义不清晰,可能与硬连接失败混淆。
修复:通过 gotHTTPResponse 标志和 budgetExhausted 标志明确区分三种结果:
- 有 HTTP 响应但不匹配 →
nil, false(服务在线,路径不符) - 所有路径超时 →
nil, false(网络不确定,不标记 unreachable) - 硬连接失败 →
nil, true(unreachable)
6. Start() 重复调用导致 close(stopCh) panic
loop() 退出时关闭 stopCh,若再次调用 Start() 使用同一已关闭的 channel,recovery goroutine 的 select 会在关闭的 channel 上立即返回,错误退出。
修复:Start() 首行重建 stopCh = make(chan struct{})。
7. recoveryDelay goroutine 阻塞进程关闭
进程关闭时,等待 recoveryDelay(可达数分钟)的 goroutine 会阻塞 wg.Wait(),导致进程无法及时退出。
修复:time.Sleep(hc.recoveryDelay) → select { case <-time.After(hc.recoveryDelay): case <-hc.stopCh: return },关闭信号到达后毫秒级退出。
附加修复
UpdateHealthPaths缓存失效:目标失去显式health_check_path时,现立即调用probeCache.invalidate(),与UpdateCredentials行为对齐checkAll()数据竞争:hc.healthPath现在锁内快照为globalHealthPath,消除并发读写竞争- API 清理:移除
probeEntry.unreachable字段和ProbeCache.set()的unreachable bool参数(死代码);ProbeCache.mu从sync.RWMutex简化为sync.Mutex
🐛 Bug Fixes
tools/reportgen — SQLite 时区归一化(17 个查询函数)
新增 toUTC() helper,在所有接受 from, to time.Time 参数的查询函数入口统一转 UTC。
根因:SQLite 以字符串存储时间戳,字符串比较是字典序。当调用方传入本地时区时间(如 +08:00),与数据库中 UTC 格式的记录比较会产生错误的过滤结果,导致图表数据为空或范围错误。
修复的函数(queries.go):
QueryKPI、QueryDailyTrend、QueryHeatmap、QueryLatencyBoxPlotByModel、QueryLatencyPercentileTrend、QueryDailyLatencyTrend、QueryUserRequestBoxPlot
修复的函数(queries_phase8.go):
QueryLatencyHistogram、QueryLatencyScatter、QueryTokenThroughputHeatmap、QueryUpstreamShare、QueryUpstreamLatencyTrend、QueryCostPerTokenTrend、QueryIORatioTrend、QueryPeakRPM、QueryModelDailyTrend、QuerySourceNodeDist
🧪 新增测试(13 个)
| 测试 | 覆盖场景 |
|---|---|
TestInjectCredential_WhitespaceKey |
纯空格 key 不产生认证头 |
TestInjectCredential_TabWhitespaceKey |
制表符 key 不产生认证头 |
TestBuildProbeURL_QueryParams |
含查询参数、Fragment 的地址正确构建探活 URL |
TestWithTimeout_UpdatesProberClient |
WithTimeout 同步更新 prober HTTP client 超时 |
TestWithTimeout_Default |
默认超时下 hc.client 与 prober 一致 |
TestIsEndpointTimeout |
6 种错误类型的分类正确性 |
TestUpdateHealthPaths_InvalidatesCache |
失去显式路径的目标缓存被清除 |
TestUpdateHealthPaths_NoInvalidationForNewTargets |
新增目标不触发无效缓存清除 |
TestDiscover_CtxExpiry_NotUnreachable |
预取消 context → nil,false 而非 unreachable |
TestIsEndpointTimeout_ContextCanceled |
context.Canceled 语义文档化 |
TestProbe_Discover_ContinuesPastEndpointTimeout |
端点超时后继续尝试下一路径 |
TestProbe_Discover_ConnectionRefusedIsUnreachable |
连接拒绝 → unreachable=true |
TestProbe_Discover_AllMethodsTimeout_NotUnreachable |
全部超时 → unreachable=false |
📊 测试统计
- 主模块:2,144 个测试,25 个包,0 FAIL
- 回归:全量
go test ./...通过
Full Changelog: v2.24.5...v2.24.6