G5 事件 trace 出口与事件时间线设计规格
| 字段 | 值 |
|---|---|
| 版本 | v0.1.0 |
| 状态 | Accepted |
| 日期 | 2026-06-12 |
| 作者 | xiang.li |
| 前置 | 前端仿真洞察工作台设计规格、G5通信评估观测与结果存储设计规格 |
变更历史
| 版本 | 日期 | 变更 |
|---|---|---|
| v0.1.0 | 2026-06-12 | 初版草稿 |
概述
本 spec 冻结 Wave 2 前两项的设计:trace 数据协议的字段级定义(事件语义模型、Arrow IPC schema、服务端出口端点契约)与 event-trace-timeline 的交互模型(track 组织、两级细节模型、性能口径)。前置 spec 已冻结四层协议语义与 Arrow IPC 选型,本 spec 在其框架内落到可实现、可验收的粒度。
背景
G5 观测框架已记录 6 类事件并以 Parquet 按结果落盘,结果行持有 trace 文件引用与级联生命周期(见前置 spec《G5通信评估观测与结果存储设计规格》)。落差在消费侧:
- 事件无读取出口:trace 文件只能整文件下载或后端脚本分析,没有按时间窗口与资源过滤的查询通道。
- 事件无可视形态:前端没有任何事件级视图,「哪条链路在哪个时段拥塞」回答不了。
- 事件配对语义未冻结:6 类事件是状态转换点记录,区间(链路忙、线程阻塞)需要由配对规则推导,该规则散落在分析脚本的隐式约定里。
名词定义
| 名词 | 定义 | 与易混淆概念的区分 |
|---|---|---|
| 事件记录 | trace 的最小单元:(时间戳, 事件类别, 资源标识, 数值, 可选明细),各字段语义由前置《G5通信评估观测与结果存储设计规格》事件类别表冻结,本 spec 不重定义 | 是状态转换的点记录,不是区间;区间由配对规则推导 |
| 事件类别 | 6 个封闭枚举:link_busy / link_idle / ostd_change / thread_blocked / thread_unblocked / hop_arrive | 新增类别属 schema 演化,须修订本 spec |
| 资源标识 | 事件所属资源的字符串 id,已编码到观测粒度:链路含 VC 维度(req/rsp 两条独立串行化队列)、线程事件含 thread 维度、hop 事件以流(src→dst)为单位;完整格式见前置 spec 事件类别表 | 不是 track 名;track 由资源标识 + 事件类别映射得到;配对与聚合均在完整资源标识字符串上进行 |
| track | 时间线上的一条水平渲染带,类型 ∈ {区间, 计数, 瞬时} | 一个资源可产生多条 track(链路资源 → 区间 track;CDMA 单元 → 计数 track) |
| track 组 | 按资源族聚合的 track 折叠单元 | 两层树:组 → track,不再嵌套 |
| 窗口查询 | 按 [start_ns, end_ns) 时间窗口 + 资源/类别过滤取事件的服务端查询 | 与「全量加载」相对;前端从不持有全量事件 |
| 聚合桶 | 窗口按等步长切分的时间段,桶内事件折叠为一条聚合行 | 服务端聚合的粒度单元;与前端像素无固定对应,由前端按视口宽度请求桶数(上限 4096,见 附录 C) |
| 细节级别 | 聚合模式(粗看全局)与原始模式(细看窗口)两级 | 不是连续 LOD 金字塔;两级间由事件数阈值切换 |
@tbl-spec-ttl-02 名词定义
目标与非目标
目标:
- 冻结事件语义模型:6 类事件的配对规则(点记录 → 区间/计数/瞬时)与边界处置。
- 冻结 trace 出口契约:窗口查询端点的输入语义、两级响应模式(原始 / 聚合)、Arrow IPC schema。
- 冻结时间线交互模型:track 映射规则、两级细节切换、缩放平移语义。
- 性能口径:10^6 事件 trace 下时间线缩放/平移 ≥ 30fps(继承前置 spec 验收标准 6),窗口查询响应 P95 ≤ 1 s。
非目标:
- 不覆盖 trace 采集与落盘(归《G5通信评估观测与结果存储设计规格》,本 spec 只读其产物)。
- 不覆盖 trace 文件生命周期管理(同上,级联删除已冻结)。
- 不为部署 G5 单芯片路径新增 trace 采集;本 spec 出口对「有 trace 文件的结果行」生效,当前即通信原语评估路径。
- 不覆盖 link-utilization-replay(3D 拓扑回放):它消费同一出口,交互设计另立 spec。
- 不做 trace 跨任务对比与 SQL 查询界面。
用例说明
用例 1:定位拥塞时段。用户打开某条有 trace 的通信评估结果,时间线以聚合模式展示全程:链路 track 组显示各链路忙闲密度,计数 track 显示未完成事务数曲线。用户发现 2.1–2.4 ms 段事务数持续高位,框选该段放大;事件数降到阈值内,时间线切换为原始模式,逐条区间可见,鼠标悬停显示单条事件明细。
用例 2:追一条链路。用户在 track 组面板取消全选,仅勾选两条链路与对应 CDMA 单元的 track。前端仅以选中资源的标识请求窗口数据,传输量与选中资源数成正比。
用例 3:阻塞归因。用户对齐查看线程阻塞 track 与链路忙闲 track:阻塞区间与链路空闲区间同时出现,得出「线程在等信用而非等带宽」的结论,转向 ostd 计数 track 验证。
详细设计
事件语义模型
事件记录是状态转换的点记录,时间线渲染需要的区间/计数/瞬时三种形态由以下配对规则推导。配对规则是协议的一部分:服务端聚合与前端渲染必须实现同一规则,规则变更须修订本 spec。
| 事件类别 | 形态 | 配对规则 |
|---|---|---|
| link_busy / link_idle | 区间 | 同一资源标识(含 VC 维度)上,busy 开启区间,下一条 idle 关闭区间 |
| thread_blocked / thread_unblocked | 区间 | 同一资源标识(含 thread 维度)上,blocked 开启、unblocked 关闭 |
| ostd_change | 计数 | 每条记录的数值为该时刻的新计数值,曲线为阶梯函数 |
| hop_arrive | 瞬时 | 每条记录独立成点,无配对;数值为本跳延迟(ns) |
@tbl-spec-ttl-01 事件类别到渲染形态的配对规则
边界处置:
- 悬空开启:区间开启后到 trace 末尾无关闭事件,区间右端取仿真结束时刻,标记为未闭合。
- 悬空关闭:窗口查询截掉了开启事件时,区间左端取窗口起点,标记为左截断;服务端窗口查询必须返回「窗口起点时刻各资源的活跃区间状态」,否则前端无法正确渲染跨窗口边界的区间(语义见 出口契约 节)。
- 乱序:同一资源标识上出现 busy-busy 或 idle-idle 相邻时,该资源该类别标记为数据异常,前端以异常态渲染该 track,不静默吞掉。
状态重建的职责分工:服务端持有全量 trace 文件,窗口起点的活跃区间状态由服务端从文件起点扫描重建;活跃区间快照是端点的输出(供前端渲染左截断区间),不是前端的输入义务。
聚合算法(规范性)
聚合模式的行语义由以下规则冻结,服务端实现与验收金标准以此为准:
- 服务端按配对规则对窗口内每个 (资源标识, 区间类别) 维护开闭状态,初始状态来自窗口起点的状态重建。
- 每桶每 (资源标识, 类别) 的聚合行:
event_count= 桶内事件数;区间类busy_ratio= 桶内被开区间覆盖的时长 ÷ 桶宽;计数类last_value= 桶末计数值(桶内无事件时延续前值)。 - 零事件桶的 emit 规则:桶内无事件但被跨桶开区间覆盖(区间类)或计数值非零延续(计数类)时,该聚合行必须 emit(
event_count= 0,busy_ratio/last_value按延续状态计算);既无事件也无延续状态的 (桶, 资源, 类别) 不 emit。 - 验收标准中「聚合行数 = 桶数 × 活跃 (资源, 类别) 数」的「活跃」定义:窗口内有事件或有延续状态的 (资源, 类别) 组合。
trace 出口契约
决策:单一窗口查询端点,两级响应模式(原始 / 聚合),响应体为 Arrow IPC stream。
输入语义(完整签名见 附录 C):
- 寻址:以结果行主键寻址 trace(trace 文件与结果行的关联由前置 spec 冻结),结果行无 trace 时 404。
- 窗口:[start_ns, end_ns),缺省为全程。
- 过滤:资源标识集合、事件类别集合,均可选;过滤在服务端完成。
- 模式:由请求的聚合桶数决定——给出桶数走聚合模式,不给走原始模式;原始模式有服务端行数上限(量级 10^5,对应单响应传输预算,具体值归实现 plan 并在 400 响应中回显),超限拒绝并附建议桶数,不静默截断。
两级响应模式:
| 模式 | 行语义 | 用途 |
|---|---|---|
| 原始模式 | 一行 = 一条事件记录,schema 与落盘 Parquet 同构 | 窗口已缩小到阈值内的细看 |
| 聚合模式 | 一行 = (桶, 资源, 类别) 的聚合:事件数、区间覆盖时长占比、计数末值 | 全程概览与大窗口粗看 |
@tbl-spec-ttl-03 两级响应模式
聚合模式同时返回窗口起点的活跃区间快照(边界处置的要求),原始模式同样携带。
设计原理:
- 查询驱动而非全量加载:前端从不持有全量事件,视口变化触发窗口查询——Perfetto 的查询驱动渲染是同构先例(见 附录 A)。选定的 Arrow IPC stream format 无 footer 索引、不支持随机访问,该局限在查询驱动模型下无影响:随机访问由服务端窗口切分承担,客户端只做顺序消费。
- 聚合在服务端:10^6 事件全程概览若传原始行,传输与解析成本随事件数线性增长;服务端按桶聚合后传输量只随桶数(视口像素量级)增长,与事件数无关。Perfetto slice mipmap 为同构先例。
- 两级而非连续 mipmap:连续多级 LOD 金字塔需预计算与缓存层,一期以「聚合粗看 + 原始细看」两级覆盖交互闭环,金字塔列入 局限与后续工作 节。
时间线交互模型
track 映射:track 由 (资源标识, 形态) 自动派生,不需要用户配置。资源标识前缀决定 track 组;组内 track 类型按事件类别的形态映射(区间 track / 计数 track / 瞬时 track)。track 组支持折叠与勾选过滤,勾选状态转换为窗口查询的资源过滤参数。三种 track 的视觉语义:区间 track 渲染矩形条,窄于 1 像素的相邻区间合并为聚合块;计数 track 渲染阶梯线,y 轴按 track 内值域自适应;瞬时 track 渲染标记点,数值(本跳延迟)以颜色深浅编码、悬停显示精确值。
两级切换:当前窗口的预估事件数(由聚合模式的桶计数累加得到)低于阈值时切原始模式,高于阈值回聚合模式;阈值与滞回参数归实现 plan。切换对用户透明,界面标注当前模式。
缩放平移:滚轮缩放以光标为锚点,拖拽平移;视口变化去抖后发起窗口查询,查询期间保留旧数据渲染并显示加载态。渲染用 Canvas 2D + 视口外 track 裁剪起步(聚合保证每 track 单帧矩形数不超过桶数量级),不达性能口径再升级 WebGL(业界演进顺序一致,见 附录 A)。
与 Gantt 的关系:时间线是事件级视图,op 级 Gantt 保持现状不动;二者不共享组件,时间轴单位统一为 ns 起算的仿真时间。
集成点
- 上游:读前置 spec 冻结的 Parquet 落盘文件与结果行 trace 引用;不要求 Rust/采集侧任何改动。
- 下游:link-utilization-replay(Wave 2 第三项)消费同一窗口查询端点的聚合模式,按桶取链路区间覆盖占比作为热力值;其交互 spec 不再重复冻结数据出口。
- 契约归属:trace 数据协议的字段级定义以本 spec 为准;前置工作台 spec 附录 C 声明的「实现后冻结回填」由本 spec 承接。
备选方案
| 决策点 | 被否方案 | 否决理由 |
|---|---|---|
| 出口形态 | 整文件下发,前端全量解析 | 10^6 事件 Parquet 数十 MB,首屏不可用;前端内存随 trace 体积线性增长,违背前置 spec「不要求全量事件常驻内存」 |
| 出口形态 | WebSocket 推送 trace 数据 | 前置 spec 已冻结「trace 本体不走 WebSocket」;窗口查询是请求-响应语义,HTTP 自然匹配 |
| 聚合位置 | 纯前端聚合(服务端只过滤) | 全程概览仍需先传全量原始行,聚合省了渲染省不了传输与解析 |
| LOD 模型 | 连续多级 mipmap 金字塔(Perfetto 式) | 需预计算与缓存层,一期复杂度不成比例;两级模式已覆盖交互闭环,金字塔保留为后续升级路径 |
| 区间表示 | 落盘时直接写区间行(start_ns + end_ns) | 改变采集侧 schema,越权修改前置 spec 冻结的落盘契约;配对在读取侧推导即可 |
| 渲染技术 | 一步到位 WebGL | 聚合后单帧矩形数在 Canvas 2D 预算内(万级),WebGL 提前引入维护成本;Perfetto 同样以 Canvas 起步后迁移 |
@tbl-spec-ttl-04 备选方案与否决理由
非功能性需求
| 维度 | 本 spec 的考虑 |
|---|---|
| 性能 | 10^6 事件 trace:时间线缩放/平移 ≥ 30fps;任一窗口查询(原始或聚合)端到端 P95 ≤ 1 s;聚合模式响应体与事件总数无关、随桶数线性 |
| 兼容性 | 落盘 Parquet schema 不变;trace 出口是只读增量端点,既有 comm-results API 不变;trace 缺失时结果页其余功能不受影响 |
| 可靠性 | 窗口查询失败显式错误态,禁止静默空时间线;原始模式超限返回明确错误与建议桶数 |
| 安全性 / 隐私 | N/A(内网自用工具) |
@tbl-spec-ttl-05 非功能性需求
局限与后续工作
- 聚合无预计算:每次聚合查询对窗口内事件实时扫描,单文件 10^6 事件在预算内;事件数再上一个量级时需引入 mipmap 预计算或聚合缓存。
- 部署 G5 路径无 trace:本出口对部署评估任务暂无数据可用,采集侧扩展归 G5 观测框架的后续 spec。
- 异常数据只标记不修复:乱序配对标记异常态,根因(采集侧丢事件或时序错误)需回 G5 观测框架排查。
- detail 明细不参与过滤:明细 JSON 仅悬停展示,不支持按明细内容查询;需要时再扩展端点。
- 时间线无书签与标注:定位结论无法持久化分享,列为候选增强。
验收标准
- 配对语义正确:构造含悬空开启、悬空关闭、乱序的 trace 样本,服务端聚合与前端渲染的区间推导结果与本 spec 配对规则一致(单测金标准比对)。
- 窗口查询正确:任意 [start_ns, end_ns) 窗口 + 资源/类别过滤的返回行集与全量过滤金标准一致;窗口起点活跃区间快照完整。
- 协议合规:用 Arrow JS 读取端点响应可还原全部字段;原始模式 schema 与落盘 Parquet 字段一一对应(前置 spec 验收标准 4 的落地口径)。
- 聚合可缩放:同一窗口请求不同桶数,聚合行数 = 桶数 × 活跃 (资源, 类别) 数,响应体大小与事件总数无关(构造 10× 事件数的 trace 验证)。
- 性能达标:10^6 事件 trace 下,时间线缩放/平移 ≥ 30fps(浏览器 performance 面板录制);窗口查询 P95 ≤ 1 s(集成测试计时)。
- 模式切换闭环:从全程聚合视图框选放大到阈值内自动进入原始模式,悬停可见单事件明细;放大缩小往返不产生请求风暴(去抖生效)。
附录 A:业界调研
track 模型与 LOD 策略对比
| 维度 | Perfetto | Chrome Trace Viewer (catapult) | speedscope | 本 spec |
|---|---|---|---|---|
| track 组织 | 两层树(process group → thread/async/counter track) | pid/tid 双层 | 单 profile 无组 | 两层树(资源族前缀 → 资源 track) |
| track 类型 | slice / counter / instant | duration(B/E) / counter(C) / instant(i) / async(b/e) | 仅调用栈 slice | 区间 / 计数 / 瞬时 |
| LOD | slice mipmap 算子,按窗口 + 步长聚合,窄于 1px 量化合并 | 全量加载,大 trace 卡顿 | GPU 渲染 + 不可读节点剔除 | 两级(服务端桶聚合 / 原始窗口) |
| 数据加载 | 查询驱动:视口变化 → trace_processor SQL 窗口查询,UI 不持全量 | 整文件 JSON 加载 | 整文件加载 | 查询驱动:视口变化 → HTTP 窗口查询 |
| 渲染 | Canvas 2D 起步,v55 起密集 track 迁 WebGL | Canvas 2D | WebGL | Canvas 2D 起步,预留 WebGL 升级 |
@tbl-spec-ttl-06 业界 trace viewer 设计对比
来源:
- Perfetto track 模型:https://perfetto.dev/docs/reference/synthetic-track-event、https://perfetto.dev/docs/instrumentation/track-events
- Perfetto slice mipmap:https://github.com/google/perfetto/blob/master/src/trace_processor/perfetto_sql/intrinsics/operators/slice_mipmap_operator.cc
- Perfetto 查询驱动与 WebGL 迁移:https://perfetto.dev/docs/analysis/trace-processor、https://github.com/google/perfetto/releases
- Chrome Trace Event Format:https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview
- speedscope 渲染:https://jamie-wong.com/post/speedscope/
- Arrow JS 流式消费:https://arrow.apache.org/docs/8.0/js/classes/Arrow_dom.RecordBatchReader.html;Arrow vs JSON 大数据加载实测:https://blog.scottlogic.com/2021/10/15/efficiently-loading-massive-d3-datasets-using-apache-arrow.html
- Canvas 2D 与 WebGL 矩形预算测评:https://semisignal.com/a-look-at-2d-vs-webgl-canvas-performance/
Arrow IPC stream vs file format
stream format 无 footer 索引、不支持随机访问,但匹配「服务端切窗口、客户端顺序消费」的查询驱动模型;file format 的随机访问价值在静态整文件下发场景,与本 spec 出口形态不符,故选 stream。
附录 B:实现说明
首版实现(2026-06-12)
- 服务端:查询纯逻辑在
services/trace_query.py(读 Parquet / 配对 / 桶聚合 / IPC 序列化),端点挂services/api/comm_results.py。契约实值:原始模式行数上限 100,000,超限 400 响应回显上限并建议桶数 2048;快照 S 与乱序异常集合经 IPC schema metadata 键trace_snapshot/trace_anomalies下发。 - 聚合实现要点:配对 / 逐桶计数 / 异常检测共享单遍 track 编码分组(NumPy 向量化);无乱序 track 走向量化配对,乱序 track 回退伪代码状态机(随机对照测试锁定两路径与 spec 伪代码逐值一致)。瞬时类(hop_arrive)聚合行仅事件桶 emit(规则 3 推论:无延续状态)。
- 计数延续简化:快照 S 仅区间类(spec 冻结),窗口起点前的计数初值不重建,last_value 从窗口内首个计数事件起向后延续,首事件所在桶之前不为该资源 emit。
- 前端:
fetchTraceWindow用 fetch + Arrow JS RecordBatchReader 流式消费;配对模型model.ts与后端金标准同值用例交叉锁定;TraceTimeline 组件 Canvas 2D 渲染,懒加载独立 chunk(45KB gzip);两级切换阈值 ≤50,000 切原始、>60,000 回聚合(滞回)。入口在通信分析任务列表(has_trace 行的「时间线」按钮)。 - 性能实测(10^6 事件合成样本,
scripts/synth_trace_perf.py):全程聚合 b=2048 P95 684ms、窗口聚合 b=1024 P95 297ms、原始模式 P95 161ms,均满足 ≤1s;10× 事件密度饱和对比下聚合响应体比值 1.00(验收标准 4)。时间线 30fps 口径待真实任务浏览器 performance 面板录制确认。 - 验收标准 4 的口径澄清:「聚合行数 = 桶数 × 活跃组合数」是上界,等号仅在活跃组合于每桶都有事件或延续状态(饱和态)时成立——规则 3 对无事件无延续的桶不 emit(稀疏 trace 聚合行更少,属正确行为)。体积不变性验证须在饱和态构造对照样本。
附录 C:完整接口签名 / 数据结构
窗口查询端点:
GET /api/evaluation/comm-results/{result_id}/trace
?start_ns=<float, 可选, 缺省 trace 起点>
&end_ns=<float, 可选, 缺省 trace 终点>
&components=<资源标识列表, 逗号分隔, 可选>
&categories=<事件类别列表, 逗号分隔, 可选, 值 ∈ 6 类枚举>
&buckets=<int, 可选; 给出走聚合模式 (1..4096), 不给走原始模式>
→ 200 Content-Type: application/vnd.apache.arrow.stream
原始模式: schema R; 聚合模式: schema G; 首个自定义 metadata 块携带活跃区间快照 S
→ 400 非法窗口 (start >= end) / 非法类别 / buckets 越界 /
原始模式窗口事件数超上限 (响应体含建议 buckets 值)
→ 404 结果行不存在或无 trace 文件
schema R(原始模式,与落盘 Parquet 同构):
| 列 | 类型 | 语义 |
|---|---|---|
| time_ns | float64 | 事件时间戳(ns) |
| category | dictionary<string> | 6 类枚举之一 |
| component | dictionary<string> | 资源标识 |
| value | float64 | 类别相关数值,语义由前置 spec 事件类别表冻结(link_busy 为本次传输串行化耗时 ns、link_idle 为 0、ostd_change 为变化后计数值、thread_blocked/unblocked 为状态值 0/1、hop_arrive 为本跳延迟 ns) |
| detail_json | string, nullable | 明细 JSON 文本 |
@tbl-spec-ttl-07 schema R 列定义
schema G(聚合模式):
| 列 | 类型 | 语义 |
|---|---|---|
| bucket_start_ns | float64 | 桶起点 |
| component | dictionary<string> | 资源标识 |
| category | dictionary<string> | 事件类别 |
| event_count | uint32 | 桶内事件数 |
| busy_ratio | float64, nullable | 区间类:桶内区间覆盖时长占桶宽比例;非区间类 null |
| last_value | float64, nullable | 计数类:桶末计数值;非计数类 null |
@tbl-spec-ttl-08 schema G 列定义
活跃区间快照 S(IPC stream 自定义 metadata,JSON;仅区间类事件需要快照,计数/瞬时类不在其中):
{ "window_start_ns": <float>,
"open_intervals": [ { "component": <str>, "category": <"link_busy"|"thread_blocked">,
"since_ns": <float> }, ... ] }
配对推导伪代码(规范性):
输入: 窗口内事件序列 + 活跃区间快照 S
(S = 窗口起点的开区间集合; 服务端从文件起点扫描全量时 S 为空集)
对每个 (component, 区间类别对):
初始化: 若 S 含该 component → open(since = window_start, 左截断标记)
按 time_ns 升序扫描:
开启事件: 若已有未闭合区间 → 标记异常; 否则 open(since = time_ns)
关闭事件: 若无未闭合区间 → 标记异常; 否则闭合 [since, time_ns)
扫描结束仍 open → 区间 [since, 窗口终点), 未闭合标记
(窗口终点 = end_ns; 全程查询时即 trace 末尾的仿真结束时刻)