Repository navigation
v3.10.5
v3.10.5
v3.10.5 — 状态机原子性:并发冷却、Key 分类、软超时副作用
PR #29。本次不发 MCP Registry。
变更
- MMR 的
target_count缺省值改为跟随max_size:smartsearch.mmr.target_count: 0(缺省)此前表示「对全部结果做重排」,而超出max_size的尾部随后必被截断丢弃,等于白做一轮二次方贪心选择。现在缺省取max_size为上限,max_size不限时仍重排全部(旧语义);显式配置target_count的行为不变。MMR 贪心选择具有前缀性(选前 k 项与对全量选择后的前 k 项相同),输出内容不变,只省掉无用工作 - URL 归一化统一为一套语义:评分增强的跨引擎去重键(
enhance.normalizeURLKey)改为委托core.NormalizeURL,与去重主链路一致——现在同样剥离utm_*/fbclid/gclid/ref/spm追踪参数并排序查询参数。行为收紧点:跨 scheme /www.的变体不再合并(与core键一致)
修复
- 并发状态迁移的原子性(P0):会话轮换、冷却档位升级、成功清理三处状态迁移此前跨锁边界「读出来-判断-写回」,并发搜索下可复现真实竞争。现在轮换(计数 + 判定 +
client/UA 重写)整体入锁、请求取用同一临界区的会话快照;冷却档位计算收敛为纯函数antirobot.NextCooldownDuration,读写在同一临界区完成;成功路径引入状态代次快照,只在「本次请求期间没有并发请求进入冷却或记录失败」时才清理对应状态。ddg/baidu/bing/google/so360/arxiv六处受影响 - 传输层瞬断不再冻结 API Key 30 分钟(P0):
KeyError此前同时表达「key 坏了」与「这次调用失败了」,apipool只要命中就MarkInvalid30 分钟——单 Key 供应商一次网络抖动即 30 分钟不可用。现在KeyError带显式分类(auth/rate_limit/transport/content),apipool按分类查表:鉴权失败冷却该 key,限流短冷却(5 分钟),传输层与内容类失败不冷却任何 key 且立即级联下一供应商 - 全部 Key 失效时不再下发已知失效的 Key:
KeyPool.Next()在池内全部冷却时返回空串,新增NextUsable()返回是否取到;8 个供应商与 mcphub 均改为「取不到即上报明确错误」,不再拿着已被冷却的 key 去打一次必然被拒的请求 - 删除并发下会标错 key 的
MarkLastInvalid:该方法标记的是「池级最近返回的 key」而非「本请求实际使用的 key」,并发下会误冻好 key、放过坏 key(代码注释早已自认非并发安全)。已整体删除,兜底路径改为按KeyError分类处理 - 软超时放弃后不再改状态(P1):编排层对超时引擎放弃等待后,引擎的 goroutine 仍继续推进冷却与会话状态,导致观测层记「超时失败」而上游记「成功」。新增
antirobot.ContextEngine可选接口(ddg/baidu/bing/google/so360/arxiv/github实现),ctx 取消后请求立即中止,且被放弃的调用不再推进冷却 / 会话轮换 / 退避状态;前置退避等待也改为可被 ctx 取消 - 重复
stop不再重复触发关闭(P1):refcount的「检查 + 更新」改为锁内完成并按饱和处理,只有真正的1 → 0迁移才发关闭信号——此前计数已为 0 时再-1(脚本重试、客户端重复 stop)会每次都重发一次关闭信号 academicsearch分页不再静默退化(P1):缓存键此前不含page,page≥2在 TTL 内会命中第一页的记录并返回第一页结果。现在页码参与缓存键- 缓存代次写入的静默丢弃变为显式报告:
Clear()与在飞写入并发时,旧代次视图的写入按设计作废,此前静默返回nil;现在返回哨兵cache.ErrStaleGeneration,mcp调用方按竞态静默处理,其它写入错误记日志 - mcphub 空结果按失败同待遇冷却:后端返回 200 但零结果(或抓取空正文)此前记进失败清单却不触发冷却,语义不对称且持续重试浪费上游配额;现在以哨兵
mcphub.ErrEmptyResults判定并参与冷却(不靠错误文本关键词匹配) - 本端限流的归因不再失真:
baidu/bing/google/github/googlenews/wikipedia/so360七个引擎在本端限流器拒绝放行时返回「空结果 + nil error」,被编排层记成「成功但零结果」——失败清单为空、聚合错误变成「所有搜索引擎均未返回有效结果」,控制中心的失败构成里完全看不到限流这一类。现在统一返回哨兵antirobot.ErrLocalRateLimit,失败清单正确产出kind=rate_limit - 评分流水线热点:分词去正则:
tokenize是评分流水线的 CPU 与分配热点(正则FindAllString的匹配状态机开销高),改为手写字节扫描,语义逐字节等价(等价性用例以旧正则实现为 oracle 覆盖中英混排、CJK 长串、emoji、ASCII 边界等语料) - 独立评审补缺口(合入前):
baidu/bing/google/so360/arxiv/github补齐SearchContext(ctx 取消后不再改退避 / 冷却 / 会话);KeyPool.MarkInvalidFor取较长截止,并发短冷却不再覆盖鉴权长冷却;DDG 本端限流改走LocalLimitSkipped;academicsearch省略 page 与 page=1 共用缓存键;TinyFish / Keenable 的 429/403 按keyErrorKindByStatus分类;NewKeyError零 Kind 强制写成 transport