Skip to content

v2.24.6 — Smart Probe 全面修复 + reportgen UTC 时区归一化

Latest

Choose a tag to compare

@l17728 l17728 released this 11 Apr 10:19
· 5 commits to main since this release

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