You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
chajia/docs/报价提速与稳定性-改动清单.md

8.6 KiB

报价提速与 API 稳定性 — 改动清单

版本2026-06-30
目标:端到端 15s 内 P50Direct 成功路径)、并发不互相挡死失败快速返回(禁 Widget 回退)。
基准:当前 probe:human-doc-system 10/10 约 2.4min~14s/单),候选 515s + 报价 520s + 队列/轮询开销。


总览(按优先级)

批次 项数 预期收益 风险 建议工期
P0 运维 5 稳定性 + 避免假慢 0.5 天
P1 小改 4 候选 -38s报价 -25s 12 天
P2 架构 2 整单 -38s去队列+轮询) 23 天
P3 隔离 2 并发稳定性 12 天

推荐实施顺序: P0 → P1-1 → P1-2 → P1-3 → P1-4 → P2-1 → P2-2 → P3按需


P0 — 运维与配置(不改代码)

ID 改动 操作 验收
P0-1 生产 Direct + 禁 Widget .envRPA_QUOTE_MODE=directRPA_DISABLE_WIDGET_QUOTE_FALLBACK=true worker 日志出现 node-direct 成功 Widget fallback
P0-2 Nginx 超时对齐 proxy_read_timeout 420s(方式 A submit submit 不在 60s 被 502420s 内正常返回
P0-3 storageState 预热的 worker 启动日志无 bootstrap 失败;.rpa/mothership-storage.json 存在且 rpa_state 卷挂载 冷启动首单不额外 +3060s
P0-4 orphan job 巡检 重启后看 已回收 N 条 orphanBull Board /admin/queues 无长期 active 连续 2 单报价第二单不无故 +30s
P0-5 宿主传完整 placeId 第二步候选对象含 option_idChIJ… worker 日志无重复 searchLocation

P1 — 小改快赢(低风险代码)

P1-1 去掉报价重复 fetchPlace

内容
根因 resolveSidefetchPlace7579 行又对同一 placeId 再 fetch 一次
文件 lib/axel/quote-from-request.ts
改法 resolveSide 返回 { placeId, description, place?: AxelPlaceLocation };已知 placeId 路径复用 place 对象search 路径只 fetch 一次
并行 pickupSide / deliverySidePromise.all
测试 扩展 __tests__/lib/axel/ 或 mock client断言 fetchPlace 每侧最多 1 次
验收 同址报价 worker 日志 place 请求减少;probe:human-doc-system 仍 10/10
预期 -13s/单

P1-2 候选 verifyCandidates 只校验 Top-K

内容
根因 每条联想都 fetchPlaceN 条候选 = N 次 HTTP
文件 lib/axel/candidates.ts
改法 新增 AXEL_CANDIDATE_VERIFY_TOP_K(默认 3);仅前 K 条 verifyCandidate,其余 selectable: true(或 unknown 由前端展示「未校验」— 推荐默认 true 保持 UX
测试 __tests__/lib/axel/candidates.test.ts5 条候选 mock 断言 fetchPlace ≤ K
验收 candidates API P50 下降;仍至少 1 条 selectable:true
预期 候选 -38s(候选多时明显)

P1-3 宿主同步接口轮询间隔缩短

内容
根因 waitForQuoteDetailPOLL_INTERVAL_MS=2000,完成后再等平均 +1s
文件 modules/host/wait-for-quote.ts;可选 lib/frontend/constants.tsHOST_POLL_INTERVAL_MS=500
改法 仅 host submit 路径用 500msembed 前端可保持 2s 减 DB 压力
测试 __tests__/modules/host/wait-for-quote.test.ts mock 时间
验收 submit 在 worker 完成后 <1s 内返回
预期 -0.51.5s/单(方式 A

P1-4 候选结果缓存(短 TTL

内容
根因 同街道重复联想仍打 MotherShip
文件 modules/address/mothership-candidates.tslib/axel/candidates.ts + modules/cache/redis-cache.ts
改法 key addr-cand:{hash(pickup+delivery)}TTL 120s,仅缓存成功结果
测试 集成测试:第二次同址命中缓存
验收 重复候选 <500msTTL 后失效
预期 重复查询 -512s

P2 — 架构提速(中风险,收益最大)

P2-1 API 层 Direct 同步报价(跳过 BullMQ 快乐路径)

内容
根因 L2 未命中必 enqueueForRpanode-direct 本可在 API 进程完成
文件 modules/quote/orchestrator.ts(主)、workers/rpa/job-handler.ts(复用 completeQuoteSuccess 逻辑抽共享函数)、lib/rpa/env.tsisInlineDirectQuoteEnabled()
改法 1) 新增 tryInlineDirectQuote(cargo)fetchAxelQuoteItems → 写 L2 + quote_record.done2) 成功则直接返回 status:done3) 失败且 RPA_DISABLE_WIDGET_QUOTE_FALLBACK=truefailed4) 失败且允许 fallback 则入队 Worker
开关 RPA_INLINE_DIRECT_QUOTE=true(生产默认开,可关回滚)
约束 仍计 1 次 MotherShip 报价;须写 L2/L3、metrics、幂等一致
测试 单测 mock axelprobe:human-doc-system;并发 2 单不串单
验收 日志 inline-direct 成功;无 queue 等待10/10 PASS
预期 整单 -38s(去队列 + 部分轮询)

P2-2 方式 A submit 合并为「创建即等待」单事务感知

内容
根因 P2-1 后 host submit 可 submitQuote 同步得到 done,无需 waitForQuoteDetail 长轮询
文件 modules/host/public-orchestrator.ts
改法 hostPublicSubmitAndWait:若 submitQuote 返回 done 直接 getQuoteDetail;仅 processing 时轮询
测试 host submit 单测
验收 submit 在 inline 成功时 0 次 DB 轮询
预期 叠加 P2-1方式 A 再 -12s

P3 — 并发与稳定性隔离

P3-1 Priority1 与 MotherShip 分队列

内容
根因 priority1-quotequote-rpa 同 Worker、concurrency=1,实验挡生产
文件 lib/constants/rpa.tsworkers/rpa/index.tsworkers/rpa/queue.ts
改法 新队列 priority1-rpa + 可选独立 worker 容器;或 Priority1 job 仅 dev 环境入队
测试 入队 priority1 时 MotherShip quote 仍可并发消费(若 P3-2
验收 生产 compose 仅 rpa-worker 消费 quote-rpa
预期 消除实验性挡队列

P3-2 node-direct 专用 Worker 并发(可选)

内容
根因 RPA_WORKER_CONCURRENCY=1 为 Playwright 设计HTTP-only 可并行
文件 workers/rpa/job-handler.tsworkers/rpa/mothership.ts
改法 仅当 job 走 fetchAxelQuoteItems 成功路径时无 session lock浏览器 fallback 仍 concurrency=1或拆 rpa-http-worker 进程 concurrency=3
风险 MotherShip 限流storageState 并发读
验收 3 并发 HTTP 报价无串扰;失败率不升
预期 高峰 P95 -1030s

观测与门禁(每批完成后必跑)

# 功能回归
npm run test:unit
npm run probe:human-doc-system

# 耗时对比(改前改后各跑 1 次,记录总秒数)
npm run probe:human-doc-system

# 服务器
curl -w "candidates: %{time_total}s\n" -X POST .../api/host/quote/candidates ...
# worker 日志关键字
docker logs <rpa-worker> 2>&1 | grep -E "node-direct|inline-direct|Widget fallback"
指标 改前参考 P1 目标 P2 目标
候选 API P50 515s 38s 38s
报价含队列P50 1025s 818s 512s
方式 A submit P50 1530s 1222s 815s
probe:human-doc-system 10/10 ~144s 10/10 ≤120s 10/10 ≤90s

不建议做

原因
生产开启 Widget fallback 612min/单,堵死 worker
仅增大超时 不解决排队与冗余 HTTP
盲目 concurrency=4 同进程 Playwright session 争用、orphan job
跳过 placeId 校验 报价 400 / 地址不可选返工更慢

implementation-plan 对应 Step

Step 对应 ID 状态
12.1 P1-1 去重 fetchPlace + 并行 resolve 待执行
12.2 P1-2 候选 Top-K verify 待执行
12.3 P1-3 host 轮询 500ms 待执行
12.4 P1-4 候选 Redis 短缓存 待执行
12.5 P2-1 inline direct quote 待执行
12.6 P2-2 host submit 免轮询 待执行
12.7 P3-1 Priority1 分队列 待执行
12.8 P3-2 HTTP worker 并发(可选) 待执行

详细执行记录在 cursor_project_rules/implementation-plan.mdc Phase 12。