Files
cmautobuy/docs/task/109-量化领取到商品页耗时并消除重复设备检查.md

107 lines
6.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 109 量化领取到商品页耗时并消除重复设备检查
- 类型:重构
- 父级大工单:#1
- 所属 MVP / 版本:MVP 后续 / 任务执行性能优化
- 状态:已完成,用户验收通过
- 日期:2026-08-10
- Gitea 工单:<http://ilaer.eicp.net:8418/chengma/cmautobuy/issues/109>
## 背景与目标
Client 领取任务后到 PDD 商品页通常需要十几秒,但原代码没有分阶段耗时,且设备
连接已经读取一次 `app_current()` 后,采集和采购打开商品页又立即重复读取。
本任务建立不含业务数据的性能日志,量化 Admin、SQLite、ADB、uiautomator2 和
商品页阶段,并在不降低安全校验的前提下去掉同一会话内的重复检查。
## 最终方案
- 新增轻量 `TaskPerformanceTrace`,使用单调时钟。任务调度器建立一次任务上下文,
设备和页面适配层在同一工作线程取得该上下文,不跨任务共享。
- 正式启动时把 JSONL 写入 `data/logs/task_performance.jsonl`,按 2 MiB 轮转并保留
3 个旧文件。字段白名单只有 `task_id`、`operation`、`duration_ms`、`result`。
- 计时覆盖 ADB 检查、Admin 领取、本地保存、uiautomator2 连接、首次应用状态、
PDD 启动/等待、打开链接、首次控件树、商品页就绪和端到端总耗时。
- `PddDeviceSession` 保存连接时已经取得的应用状态。采集和采购打开商品页时复用该
瞬时结果,不再紧邻执行第二次 `app_current()`。
- PDD 已由应用状态确认在前台时跳过固定 `app_wait()`;需要启动时仍启动并等待。
打开链接后仍重新读取应用、控件树和商品页,验证码、风控、登录及采购门禁不变。
- 显式 ADB 检查当前每轮只有一次,因此保留并计时,没有删除新鲜连接校验。
- 新增只读真机测量工具,分别执行冷/热启动。工具不点击规格、下单或付款,遇到
登录或安全验证会停止。
实现与建单方案一致。额外增加独立测量工具,用于重复取得同环境中位数和 P95;
没有修改线程架构、Admin 接口、数据库、任务状态或 `pdd_data`。
## 真机测量与结论
同一台 Wi-Fi ADB 设备、同一商品页各执行 5 次。单位为毫秒,中位数 / P95:
| 阶段 | 冷启动 | 热启动 |
|---|---:|---:|
| ADB 设备检查 | 16 / 16 | 15 / 15 |
| `uiautomator2.connect()` | 234 / 297 | 203 / 250 |
| 首次 `app_current()` | 10593 / 10625 | 10578 / 10688 |
| PDD 启动或等待 | 1016 / 1172 | 906 / 969 |
| `open_url()` | 110 / 140 | 157 / 188 |
| 首次 `dump_hierarchy()` | 266 / 375 | 265 / 281 |
| 商品页就绪等待 | 2172 / 2281 | 2172 / 2266 |
| 端到端总耗时 | 14375 / 14516 | 14328 / 14422 |
嵌套阶段不能直接相加:商品页就绪包含控件树读取,端到端又包含全部阶段。这台
OPPO/ColorOS 即使 PDD 位于屏幕前台,`app_current()` 仍可能报告系统设置页,
所以热启动的启动/等待仍约 0.9 秒;控件树包名兜底继续保留。
原流程相邻调用两次 `app_current()`,本次按热启动中位数预计减少约 10.58 秒。
优化后 `uiautomator2.connect()` 仅 203 毫秒,约占端到端 1.4%,未达到 #110 的
2 秒或 20% 门槛。因此 #110 判定 No-Go,不实施持久 Device/QThread 重构。
## 改了哪些
- `client/src/performance_timing.py`:性能上下文、白名单记录和轮转日志。
- `client/buyer_main.py`:正式启动时初始化性能日志。
- `client/src/task_dispatcher.py`:记录 ADB、领取和本地保存,并绑定任务上下文。
- `client/src/pdd_device_service.py`:区分连接和首次应用状态计时,保存瞬时结果。
- `client/src/pdd_collect_service.py`:复用首次状态并记录采集商品页阶段。
- `client/src/pdd_u2_purchase_adapter.py`:复用首次状态并记录采购商品页阶段。
- `client/tools/measure_goods_open.py`:安全的冷/热启动真机测量工具。
- `client/test/test_performance_timing.py`:验证单调计时、任务隔离和日志白名单。
- 相关设备、采集、采购和调度测试:验证冷/热启动、去重及安全行为。
- `docs/client/06-quality-security.md`:记录日志字段、阶段、嵌套含义和测量方法。
## 验收结果
| 验收标准 | 结果 |
|---|---|
| 每次正式任务记录规定阶段和端到端耗时 | 通过 |
| 单调时钟非负、任务隔离,嵌套差异有文档解释 | 通过 |
| 日志使用四字段白名单,不包含控件树、URL、异常正文或凭据 | 通过 |
| 复用首次应用状态,PDD 已在前台时跳过固定等待 | 通过 |
| 设备、页面、安全页和采购门禁保持有效 | 通过 |
| 同环境完成 5 次冷启动和 5 次热启动 | 通过 |
| #110 Go/No-Go 有数据依据 | 通过,No-Go |
| 专项、Client 全量测试和语法检查通过 | 通过 |
| 未启用真实下单,未执行下单或付款 | 通过 |
## 测试
- 执行的命令:
- `C:/Python310/python.exe -m pytest test/test_performance_timing.py test/test_pdd_device_service.py test/test_pdd_collect_service.py test/test_pdd_u2_purchase_adapter.py test/test_task_dispatcher.py -q`
- `C:/Python310/python.exe -m pytest test -q`
- `C:/Python310/python.exe -m unittest discover -s test -p "test_*.py"`
- `C:/Python310/python.exe -m py_compile buyer_main.py src/performance_timing.py src/task_dispatcher.py src/pdd_device_service.py src/pdd_collect_service.py src/pdd_u2_purchase_adapter.py tools/measure_goods_open.py`
- `C:/Python310/python.exe tools/measure_goods_open.py <设备号> <商品链接> --runs 5`
- `git diff --check`
- 结果:专项测试 59 项通过;Client 全量 pytest 311 项通过;unittest 返回 `OK`;
语法和差异检查通过;真机冷启动 5 次、热启动 5 次完成。
- **没验证到的部分**:没有领取 10 条真实 Admin 任务,因此 Admin 请求和 SQLite
保存的真机端到端日志由 Mock/临时 SQLite 自动化测试覆盖;没有在真机上主动触发
验证码、风控或登录失效,仅由既有单元测试和 XML 固件覆盖;离屏主窗口能够创建
并输出 `OK`,但立即关闭时出现既有在线更新 Worker 的迟到回调警告,本工单未修改
在线更新或窗口关闭流程。
## 相关提交
- `ba0706d` perf: 量化商品页打开耗时并去重检查 (#109)