netx/docs/reviews/biz-compare-review-2026-10-10.md

12 KiB
Raw Blame History

业务对比检查与优化记录

检查日期:2026-10-10。范围:网络管理 → 割接对比 → 业务对比,包括前端任务/批次/结果界面、Python 与 PostgreSQL 对比路径、结果分页与搜索、短期列表缓存。

本次直接修改项目代码并添加回归测试,没有连接设备执行采集,也没有修改生产数据库。浏览器截图与交互测试使用固定 API 响应。

已修复的问题

1. 搜索会改变业务判定(P1)

原实现分别按搜索条件过滤割接前、后的原表,再重新对比。例如 state=up → down,搜索 up 后只剩前侧,原本的“变化”就可能变成“删除”。重复身份键的业务还可能在筛选后错位配对。

现在:已存储的判定直接在结果表分页筛选;需要回查未保存全量明细的成功行时,先找到候选身份键,再补齐两侧的完整分组,按原有配对规则对比,最后筛选。达到读取上限的分组整体跳过,并返回截断标记,避免产生虚假的新增或删除。

2. PostgreSQL 搜索漏用部分模板过滤条件(P1)

原搜索只下推 SQL 支持的过滤规则,对 age_timer 等不支持的规则直接忽略,可能将静态 ARP 等不属于模板范围的数据纳入结果。现在不支持下推的规则走流式读取并在 Python 中完整过滤。

3. 快速切换时旧请求覆盖当前任务(P1)

前后采集任务的批次列表、打开任务、加载历史结果以及轮询缺少完整的过期保护。现在对批次请求使用清理标记,对任务与结果使用请求序号;关闭弹窗、切换路由或打开其他任务后,旧响应不能覆盖当前界面。

4. 结果加载错误容易被误读(P2)

原查询失败时保留上一组明细,界面已经显示新筛选条件;切到未完成的表后,加载状态也可能无法结束。现在加载和失败都有独立状态,错误时清除旧明细并提供重试入口;切换表/页签后清理加载状态。

5. LLDP 搜索漏查(P2)

LLDP 数据存于 biz_state_lldp_neighbor,原搜索只读 biz_state_metric_row。现在原表回查与精简结果的字段搜索都支持 LLDP 表。

6. 其他已修复边界

  • 恰好命中读取上限时,原非 PostgreSQL 路径错误地报告“已截断”;现在多读一条判断。
  • 无效或有歧义的表标识可能落到其他表;现在返回 sheet_not_found。
  • %、_ 等搜索字符按字面值匹配,避免不同查询路径含义不一致。
  • 关闭最后一项检查后,空数组被解释为“启用全部”;现在阻止关闭最后一项并提示至少保留一项。
  • 两个 HeroUI Input 使用了 isDisabled,实际需要 disabled;过滤值与容差输入的禁用状态已修正。
  • 列表缓存缺少并发合并,慢请求可以覆盖强制刷新结果或在失效后重新填充缓存;现在共享请求、保护失效状态并抑制过期的后台通知。
  • 清理中英文文案中的重复键,解决相关类型检查错误。
  • 操作后任务选择“同操作前”时原来没有加载批次列表;现在按实际使用的任务读取并共享缓存。切换采集任务时清除原批次选择,避免把上一个任务的批次带入配置。
  • 原通用下拉框嵌套了两个 label,标签不能正确关联控件;现在使用明确的 htmlFor/id,并关联提示文本。

性能改动与实际收益范围

  • 合并两套轮询,下一轮在本轮请求完成后启动。模拟浏览器测试中,运行任务在 5.2 秒内历史列表和详情各请求 2–3 次(包含可能尚未完成的初始加载),没有重复轮询,也不会因慢请求持续叠加。
  • 已保存的失败、新增、变化等结果在数据库中筛选并分页,避免每次翻页重新加载和对比原始批次。回归测试明确验证这一查询路径不调用 compare_rows。
  • 同一前后采集任务的批次请求共用短期缓存,列表后台刷新合并同一请求;6 项缓存测试覆盖并发、强制刷新、失败重试与失效竞态。
  • 模板查找使用 Map,避免列表搜索与绘制逐项线性查找模板。
  • 修改筛选后先回到第一页,避免先请求旧页码再重复请求第一页。

这些改动减少了重复工作。以下后续基准使用合成数据,没有测量生产环境延迟。

后续优化(2026-10-10)

历史执行配置与精简行还原

新运行在创建时将模板、启用的检查项、字段规则/过滤、接口归一化、端口映射、成功行保存策略和名称存入 summary_json.config_snapshot。后台执行、成功行回查、精简行还原以及导出共用该配置;排队之后修改配置,甚至删除原模板/映射,也不会改变这一运行使用的配置。无需数据库结构迁移。

修复了精简存储明细还原时将 mapped_before 直接设为原始 before 的问题。现在前侧依次归一化和映射,后侧归一化,保持与完整结果的显示及 CSV 一致。ZIP 新增 config_snapshot.json,便于核对当时配置;详情接口只返回快照模板与版本,不将完整端口映射加入轮询响应。

页面显示“执行配置已保存”;旧记录显示“旧记录:未保存执行配置”。旧运行无法可靠补造当时的配置,仍保留兼容查询,成功行回查及精简行还原会使用当前配置。快照也不保存原始采集行的副本,若原始批次数据被清理,依赖行 ID 的还原仍会缺少内容。

Python 大表内存

成功样本按“层内序号、层首次出现顺序”排序,只保留样本上限内的候选和分层状态,避免先积累全部成功行。保留原分层轮询顺序;未截断的小表仍保持采集顺序。不需要接口变换时复用输入行;有变换时创建新行,不修改输入数据。稳定身份键顺序直接使用分组字典,省去再次计算全表键、额外键列表与集合。

每侧 1,000,000 行、50 个邻居分层、全部成功、保存 5,000 条精简样本的测试:优化前引擎新增分配峰值 1144.43 MiB,优化后 420.49 MiB,下降约 63.3%。完整结果与样本顺序的 SHA-256 一致。开启 tracemalloc 时耗时为 92.400 秒和 63.681 秒;该计时含内存追踪开销,不代表生产耗时。输入行在追踪开始前创建,峰值不包含输入行、数据库加载或 Python 进程的总内存。

基线为 afa8e06b2de64100f963ae30b3babfcb5d7009f4 中的对比引擎。原始指标见 百万行测试结果。身份键分组仍需要随原表行数增长的内存;这次不是完全流式对比。

大表导出

导出改为按 (seq, id) 游标分批查询、分批还原精简行、逐行将 UTF-8 CSV 写入 ZIP,避免将全部明细与完整 CSV 同时保存在内存。相同 seq 的多条结果按 id 连续读取,不漏掉跨批次的同行序号记录。保留 BOM、字段顺序及逗号/引号/换行转义。最终压缩包仍由现有接口在内存中返回,压缩文件本身的大小仍计入内存。

UI 改动

任务列表、创建向导、任务配置、对比批次和结果区统一使用深色平面、细边框、小圆角、低饱和蓝色和等宽数字。任务配置按作业参数、前后数据源、检查范围、端口映射四个区块组织;数据源左右配对,显示设备地址和采集数量。保存配置作为配置页主操作,删除操作降低视觉权重。

批次页增加最近 50 条记录的统计、刷新、加载、错误重试;刷新失败保留已载入记录。已完成只表示执行结束,业务失败/成功/新增数量单独显示。排队与运行使用不同文案,未知进度使用不确定进度条;时间、批次标识和前后采集信息便于核查。页签支持方向键及 Home/End,窄屏配置改为单列,批次表在独立容器中横向滚动。

结果区分为当前检查项/全表通过率、结果类型、搜索与辅助操作三层。增加明确的分页范围、清空筛选、加载状态、错误重试和搜索无结果提示。筛选按钮增加按下状态,字段筛选增加展开状态与键盘焦点提示。后续增加了执行配置保存状态及旧记录限制提示。

窄屏检查项导航改为紧凑的横向排列,避免一个检查项占据整行的大块区域。浏览器检查覆盖 1440px、768px、390px;筛选栏没有横向溢出。结果表保留必要的横向滚动。

验证

  • Python:87 项通过,1 项百万行基准默认跳过;单独启用百万行基准后通过。新增测试覆盖搜索、历史配置快照、有界抽样与游标导出。
  • 缓存:6 项 Node 测试通过。
  • 浏览器:27 个场景通过,包括任务列表文案、配置保存数据、同任务批次加载与切换清理、自动模式、检查项开关、创建向导、键盘页签、批次统计/状态/刷新/错误重试、配置保存状态、旧记录提示、结果布局、筛选、窄屏布局、旧任务响应保护、单套轮询、无运行时异常。
  • Vite 生产打包成功。
  • 本次业务对比页面的类型错误已经修复。全项目 tsc -b 仍被 BizStatePage.tsx、BizMigrationPage.tsx 中原有的类型/未使用变量错误阻塞。
  • 页面 ESLint 原有 34 个错误,主要是 any 与现有 React Hook 写法;批次列表增加类型后减少了 any,本次未增加错误。结果请求依赖警告已修正。通用下拉框和缓存模块没有 lint 错误。
  • PostgreSQL 路径验证了 SQL 编译与规则降级分支,没有在真实 PostgreSQL 数据库上执行查询或测量执行计划。

可重复执行的检查:

# 项目根目录
.venv\Scripts\python.exe -m pytest tests/test_biz_state_compare_export.py tests/test_biz_state_compare_snapshot.py tests/test_biz_state_compare_memory.py tests/test_biz_state_compare_search.py tests/test_biz_state_compare.py tests/test_biz_state_compare_sql.py tests/test_biz_state_compare_recovery.py -q

# 可选:百万行基准,耗时约数分钟,内存占用较大
$env:NETX_BENCH_ROWS = '1000000'
$env:NETX_BENCH_REF = 'afa8e06b2de64100f963ae30b3babfcb5d7009f4'
.venv\Scripts\python.exe -m pytest tests/test_biz_state_compare_memory.py::test_memory_benchmark_against_git_baseline -q -s
Remove-Item Env:NETX_BENCH_ROWS, Env:NETX_BENCH_REF

# web 目录
node --experimental-strip-types --test tests/cutoverDataCache.test.mjs
node node_modules/vite/bin/vite.js build
node node_modules/typescript/bin/tsc -b

浏览器脚本为 web/tests/bizCompare.browser.mjs。先启动本地 Vite 服务,再设置 NETX_PLAYWRIGHT_MODULE 为可用的 Playwright 包路径;NETX_TEST_URL 默认是 http://127.0.0.1:5179,浏览器默认使用已安装的 Chrome。所有 /v1 请求由脚本提供固定响应,不会访问实际设备或数据库。

后续值得优化的部分

  1. Python 大表分组。 有界抽样和冗余复制优化已完成,百万行基准下降约 63% 的引擎新增分配峰值。分组与输入仍随原表规模增长;进一步降低内存可研究排序流式配对或分区执行,并保持重复键配对语义。
  2. PostgreSQL 搜索与深分页。 JSON 字段包含搜索和精确总数统计仍可能扫描大量结果。需要用真实数据查看 EXPLAIN (ANALYZE, BUFFERS),再决定常用字段索引、搜索文本索引或游标分页;本次没有盲目添加生产索引。
  3. 历史配置与原始数据保留。 新运行的执行配置快照已完成。旧运行仍无法还原未记录的当时配置;精简结果还原依赖原批次行,后续可为批次清理增加引用保护或保留策略。
  4. 重复键的业务语义。 引擎按采集顺序配对同键行。如果业务应按集合判断,CLI 行顺序改变可能产生多条“不一致”。当前保留原有语义;可优先完善身份键,或另行实现并测试 Python/SQL 一致的集合匹配。
  5. 原表回查的数量上限。 每侧候选命中最多 2000 条,完整分组补齐每侧预算 10000 条,最终展示最多 200 对;这不是全表匹配总数。界面会提示截断,大范围检索需要收窄条件。