背景
当前扫描相关页面存在明显性能问题,慢点主要集中在 Candidates、Overview、statusView 和实时 Monitoring。根据浏览器抓包与运行时观察,瓶颈主要在 Node/PostgreSQL 侧的数据加载与聚合方式,而不是网络传输或 Cloudflare。
主要瓶颈
1. Candidates 没有在数据库分页
文件:packages/server/src/services/scan/api/candidate-records.ts:568
当前实现会先加载这个 job 的:
- 所有 candidate
- 所有 analysis result
- 所有 verification result
- 所有 triage result
然后在 Node 中做过滤、排序,最后才 slice() 出 20 条。数据量越大,响应越慢。
观测耗时:约 102 秒。
2. Overview 查询了所有 task 的完整 output
文件:packages/server/src/services/scan/home-overview.ts:105
首页汇总会加载 organization 下所有 job 的 task,并且选择了:
首页统计基本不需要完整 task output。大量 JSON 从 PostgreSQL 传到 Node,是 scan.homeOverview 耗时约 17 秒的重要原因。
3. statusView 每次重新构造整个 job 状态
文件:packages/server/src/services/scan.ts:4628
当前实现每次都会同时加载所有 candidate 和三类结果,还会:
- 构造完整 pipeline context
- 获取每个 stage concurrency
- 查询 BullMQ queue counts
- 在 Node 中构建多个
Map 和聚合结果
观测耗时:单次刷新约 63.5 秒。
4. Monitoring 重复完整扫描 JSONL
每个打开的网页都会独立创建 WebSocket,并且每 1.3 秒完整读取所有运行中 task 的 JSONL。首个 Monitoring 样本约 9.6 秒,同时持续占用 Node CPU。
5. 请求已经开始堆积
HAR 中出现:
scan.candidates:101.97 秒
scan.terminalTasks:99.84 秒
scan.one:95.70 秒
后两个响应体只有约 1.8 KB 和 5.8 KB,说明慢点不是网络传输,而是请求在 Node/PostgreSQL 中排队。
运行时证据
测试期间:
- Vulseek Node CPU:103%
- PostgreSQL CPU:89.5%
- Node RSS:约 1.4 GB -> 2.08 GB
- 容器内存:约 23.4 GB -> 24.4 GB
Cloudflare 不是主要瓶颈。HAR 中绝大部分时间都耗在等待服务端响应,实际接收响应只需很短时间。
Files 和 Projects 相对正常,说明慢点集中在扫描汇总、任务状态、Candidates 和实时 Monitoring。
建议优化方向
- Candidates 改为数据库侧分页、过滤、排序,避免全量加载后在 Node 中
slice()
- Overview 查询移除不必要的
tasks.output 读取,仅选择首页统计真正需要的字段
statusView 拆分为更细粒度的数据来源,避免每次全量重建 job 状态
- 对 queue counts、stage concurrency、聚合结果做缓存或快照化
- Monitoring 改成增量读取、游标式追踪或共享订阅,避免每个 WebSocket 周期性完整扫描 JSONL
- 排查是否存在少数热点 SQL、缺失索引、无界
IN (...) 或 JSON 反序列化放大问题
验收标准
scan.candidates 在大 job 下不再进行 Node 侧全量分页
scan.homeOverview 不再加载首页统计不需要的完整 task output JSON
scan.statusView 响应时间显著下降,并避免重复构造全量 job 聚合状态
- Monitoring 不再为每个前端连接重复完整扫描运行中 task 的 JSONL
- 在相同测试数据下,页面请求不再出现接近 100 秒的排队等待
证据文件
/tmp/vulseek-perf/initial.har
/tmp/vulseek-perf/job-tabs.har
agent-browser 会话已关闭。
背景
当前扫描相关页面存在明显性能问题,慢点主要集中在 Candidates、Overview、statusView 和实时 Monitoring。根据浏览器抓包与运行时观察,瓶颈主要在 Node/PostgreSQL 侧的数据加载与聚合方式,而不是网络传输或 Cloudflare。
主要瓶颈
1. Candidates 没有在数据库分页
文件:
packages/server/src/services/scan/api/candidate-records.ts:568当前实现会先加载这个 job 的:
然后在 Node 中做过滤、排序,最后才
slice()出 20 条。数据量越大,响应越慢。观测耗时:约 102 秒。
2. Overview 查询了所有 task 的完整 output
文件:
packages/server/src/services/scan/home-overview.ts:105首页汇总会加载 organization 下所有 job 的 task,并且选择了:
output: tasks.output首页统计基本不需要完整 task output。大量 JSON 从 PostgreSQL 传到 Node,是
scan.homeOverview耗时约 17 秒的重要原因。3.
statusView每次重新构造整个 job 状态文件:
packages/server/src/services/scan.ts:4628当前实现每次都会同时加载所有 candidate 和三类结果,还会:
Map和聚合结果观测耗时:单次刷新约 63.5 秒。
4. Monitoring 重复完整扫描 JSONL
每个打开的网页都会独立创建 WebSocket,并且每 1.3 秒完整读取所有运行中 task 的 JSONL。首个 Monitoring 样本约 9.6 秒,同时持续占用 Node CPU。
5. 请求已经开始堆积
HAR 中出现:
scan.candidates:101.97 秒scan.terminalTasks:99.84 秒scan.one:95.70 秒后两个响应体只有约 1.8 KB 和 5.8 KB,说明慢点不是网络传输,而是请求在 Node/PostgreSQL 中排队。
运行时证据
测试期间:
Cloudflare 不是主要瓶颈。HAR 中绝大部分时间都耗在等待服务端响应,实际接收响应只需很短时间。
Files 和 Projects 相对正常,说明慢点集中在扫描汇总、任务状态、Candidates 和实时 Monitoring。
建议优化方向
slice()tasks.output读取,仅选择首页统计真正需要的字段statusView拆分为更细粒度的数据来源,避免每次全量重建 job 状态IN (...)或 JSON 反序列化放大问题验收标准
scan.candidates在大 job 下不再进行 Node 侧全量分页scan.homeOverview不再加载首页统计不需要的完整 task output JSONscan.statusView响应时间显著下降,并避免重复构造全量 job 聚合状态证据文件
/tmp/vulseek-perf/initial.har/tmp/vulseek-perf/job-tabs.haragent-browser会话已关闭。