Files
Endoleap/PERFORMANCE.md
T

14 KiB
Raw Blame History

画布打开与空闲性能检查(2026-11-06)

本次证据

用户提供的开发日志中,画布页面路由多数约 20ms;物料元数据读取约 200–1,646ms,完整画布 GET 出现 190ms、2.4s、5.1s、11.4s 等样本,PATCH 约 1.4–4.1s。路由渲染与远程数据读取是两个阶段,不能以路由 200 很快判断画布已经可用。

日志只有 Next application-code 总时间,包含代理请求上游、响应体传输及 JSON 处理,不能据此断言是数据库 SQL 或本机渲染卡顿。既有鉴权令牌没有读取,本轮没有对用户线上画布做测试写入或生成。

本次修改

  • 画布编辑器改为独立动态模块,路由客户端先启动元数据/快照读取,同时加载编辑器代码。元数据和快照继续并行读取,不引入第二段等待。
  • 成功的 Canvas GET 在同源代理直接流式转发,不在 Next 中先缓冲完整 JSON,再解析并重新编码。ETag、schema、HttpOnly 会话续期和 no-store 保留;登录兑换响应仍按原逻辑移除凭证。
  • Canvas 流式响应的 Server-Timing 分别报告上游响应头等待和代理准备时间。它们不包含后续完整响应体传输时间,不把 proxy_headers 当作整个请求完成时间。
  • 空闲 AI 任务发现改为每 30 秒;有任务或尚待合并结果时每 1.2 秒轮询。发起生成、取消、应用结果会立即唤醒;隐藏标签页暂停网络查询,切回立即检查。
  • 没有新事件/状态时不再替换 runs/activeEdges 状态,避免空闲轮询反复触发画布渲染。
  • 侧栏请求 include_subgraphs=true 并完成分页,以 parent_board_id 建立可展开目录。它读取物料摘要,不下载每个子画布快照。创建、解散和撤销关系变更后刷新目录。

验证方法与边界

  • 单元测试覆盖子图分页、层级、缺失父项、循环防护、搜索路径、当前子图祖先,以及元数据/快照同时开始读取。
  • DOM 回归使用实际目录组件和 AI hook,验证展开/收起、当前路径、空闲 30 秒调度、空响应无重渲染、隐藏暂停及可见恢复;保留原有生成、取消、跨图连线和事务回归。
  • API 冒烟让模拟上游发送快照开头后暂停,在收到代理首段后才释放剩余响应,以证明代理没有等待完整 JSON;同时检查版本、schema 和会话续期。
  • 生产构建在临时副本进行,不改动当前开发服务器产物。最终执行结果记录于 VALIDATION.md。

本轮没有真实登录会话的公网前后对照,也没有浏览器视觉/手感验收,因此不能给出“线上打开从 11.4 秒降至某个时间”的结论。若更新后仍等待数秒,应结合 Network 中 Server-Timing 的 upstream 值、响应体下载时间和体积进一步追踪公网链路/后端;前端减少重复工作不等于消除了上游延迟。

2026-11-06:传输中断与误保存实测

旧 15 秒 deadline 同时约束响应头和整个响应体。流式响应先发出 200 后,正文仍可能被该 deadline 中断;此前客户端把 JSON 读取失败吞成 null,随后误报 invalid_canvas_snapshot。已改为响应头 15 秒、正文总计 90 秒、单次无数据等待 30 秒,并保留客户端取消。读取中断和 JSON 格式错误分别报告,不制造空快照。

[canvas-transfer] 只记录 trace_id、资源路径、毫秒、字节数、chunk 数、结果/错误类型;不记录令牌、Cookie、节点内容或图片数据。first_chunk_ms 为收到响应头后到首段正文的时间;transfer_ms 为代理消费正文的时间,包含链路和下游背压,不能独自区分网络节点。

用户真实父画布样本(约 477,400 字节,466 KiB):

样本 响应头前 正文 合计 结果
e342dd5b 707ms 25.925s 26.632s 读到 364,538 字节后 ResponseAborted
000949da 632ms 32.119s 32.751s 完整 477,441 字节
e33e4a53 713ms 1.951s 2.663s 完整 477,385 字节
7b5e0787 956ms 15.407s 16.363s 读到 282,618 字节后 ResponseAborted
4f77aafc 657ms 22.591s 23.248s 完整 477,385 字节

同一画布大小基本不变,正文平均吞吐从约 14.5 KiB/s 到约 239 KiB/s 波动。慢样本大部分时间在正文传输,而非页面路由(17–39ms)或首响应前。ResponseAborted 表示连接/消费端被取消,不能等同于我们设置的 TimeoutError,也不能仅凭它判定用户主动点击取消。开发热更新可取消旧请求,但具体请求的取消来源尚未证实。

子画布只返回 727 字节,正文约 1ms;首响应等待约 0.27–1.46s。未携带凭据的健康检查三个新连接样本:DNS 1.6–1.9ms、TCP 累计约 190–197ms、TLS 累计约 465–1,454ms、首字节累计约 723–1,842ms。健康检查只能作小响应链路基线,不能代替登录后的快照或 SQL 测量。

为什么只看页面也会 PATCH

实际浏览器 [canvas-dirty] 抓到多次仅 viewport=true,nodes=[]、edges=false 的保存。原保存器把视角变化与内容变化一同比较,因此平移、缩放也上传整个约 466 KiB 快照。已改为内容指纹决定是否 PATCH;只改变视角时保存在独立本机键中,不上传,也不产生内容恢复冲突。节点/连线发生真实修改时仍按版本保存完整快照。初始化的无来源视口回调、微小浮点差异和非用户尺寸测量均不作为服务器内容修改。

新的 [canvas-save] 样本中,客户端请求体读取 1.5–7.5ms、JSON 编码 1.6–2.1ms、上传到上游并等待响应头 334–1,440ms、响应体读取解析 0.6–0.8ms;请求约 477,370 字节。中间阶段包含上传、后端排队/SQL/提交和返回链路,前端不能进一步给出各自占比。

尚需服务端对照

  • Core canvas GET 调用 lockMaterial,源码先对 workspace 行 FOR UPDATE,且事务延续到 reply 返回。这是可能的同空间排队点;当前慢请求首响应前不足 1 秒,不能将其当作 22–32 秒正文慢的已证实主因。
  • 需 ECS 本机与公网读取同一快照的分段对照,并记录数据库取数、JSON 编码、写响应耗时及出口/网关限速。Gitea 与 API 解析到不同 IP,不能把 Gitea 大文件测试冒充同服务器对照。
  • 上传源码仍支持将图片内嵌为 data URL,可能放大快照;该父画布的实际图片占比尚需只统计字节、不输出内容的检查。迁到 managed assets 可让结构与图片独立读取,但不得为此在打开页面时暗中发起修改。
  • 此次修复没有宣称公网传输速度已经稳定,也未修改 Core 或用户线上图片存储。

同步服务端对照:2026-11-06 13:23 UTC

父画布 trace 1462ea3c:13:23:04.973 → 13:23:21.652 UTC,首响应 219.8ms,正文 16,458.6ms,共 477,388 字节。服务端记录位于 /tmp/endoleap-canvas-diag-20261106T131903Z(只含计数、TCP 状态与聚合等待,不含 HTTP 正文/令牌)。

  • 公网 TCP RTT 约 172ms,累计重传从 1 增至 31,拥塞窗口最低 2 个报文段,发送队列最高 138,240 字节;包丢失/重传导致 CUBIC 反复收缩窗口,解释了长正文传输和吞吐波动。
  • 内部 Core→Traefik 链路出现接收窗口背压,但最后完成约 481KB 的应用响应;不是数据库在逐字节生成 16 秒的数据。
  • 对齐窗口的 PostgreSQL 聚合记录没有持续 Lock 等待;只捕获一次约 154ms 的活动。只读 EXPLAIN ANALYZE 的本机快照体积计算执行 6.23ms、规划 11.4ms,不能将几十秒归因于该 SQL。
  • 同期 eth0 出口每秒约 14–62KB,无宿主队列丢弃增量;这不足以判定具体哪一段运营商或云端限速产生丢包,未擅自更改共享反代/内核网络配置。
  • 数据库聚合确认父图含 9 个节点、2 条边、2 张内嵌图片。JSON 文本约 477,646 字节,图片 src 合计 474,073 字符,约占 99.3%。原先设计让两张图片阻塞整张画布的数据读取。

修复采用普通 personal SaaS 附件模式:一份原始附件、一份按需显示预览,快照仅引用附件。前端添加真实附件上传及显式旧图迁移;Core PR #15 提供单一 1024px 预览、权限检查和有界缓存。原图不替换,AI 继续取原始内容。图片独立加载后,结构读取不再受两张完整图片的传输时间阻塞。生产部署及指定画布的迁移、重开实测见下方记录。

后端 PR #15 已由用户合并为 b2ae48e,分支/PR #936/#937、主线 #940、部署 #941 均成功。Endoleap main 570cd53 接入,776b665 补充迁移确认状态。

生产迁移与重开验收(2026-11-06,Asia/Shanghai)

用户明确点击“优化图片加载”后,父图版本 252 → 253。数据库 JSON 文本从 477,397 降至 3,708 字节(减少 99.22%);HTTP 紧凑响应为 3,471 字节,两种序列化口径不同。两张图片均不再内嵌,原始附件共 355,519 字节,其 SHA-256 与迁移前解码字节全部一致。8 个父图节点的 ID、几何及非图片引用内容哈希、边数组哈希全部一致。基线和验收摘要在服务器诊断目录 migration-baseline.json / migration-audit.json,仅含聚合和哈希。

  • 22:27:35 的迁移保存:请求 3,456 字节,成功 200,耗时 992ms。
  • 22:28:54 的成功重开,trace bb47dc97:首响应 218.3ms,正文 1.3ms,总计 219.6ms,3,471 字节。对照此前 trace 1462ea3c 的 16,678.4ms / 正文 16,458.6ms;属于两次实测,不代表稳定延迟保证。
  • 子图读取 260.4ms;两张独立图片分别 36,958 / 125,154 字节、551.2 / 1,354.9ms,均完整完成,不再阻塞画布结构下载。这些不是浏览器绘制耗时。
  • 22:28:49 另有一次 1,863.4ms 后、零正文的 ResponseAborted,随后出现新的页面初始化与成功请求。日志不能单独证明取消的具体原因,不将它描述为 15 秒超时或无错误。
  • 重新打开后没有新增 canvas-save / canvas-dirty 日志,服务端仍为版本 253;原图与结构哈希再次一致。本次只读重开未触发 PATCH。

本次修复消除了内嵌图片对整个快照的阻塞、统一短截止造成的正文中断与视角变化触发全量保存。公网链路仍可能抖动;已确认 TCP 重传及拥塞窗口收缩,但未定位具体物理丢包跳点,也未更改共享反代或内核配置。不能声称网络丢包已被修复。

迁移验收后按用户要求删除临时开发功能:页面不再显示“优化图片加载”,前端不再包含其迁移执行入口、状态和 Base64 转附件工具。已上传的原图与画布引用保持不变,后续正常图片上传和预览继续使用附件接口。

跨分区拖放的串行等待

用户 22:35:17 / 22:35:20 操作分别先 PATCH 坐标(626.5 / 213.9ms),再 POST members(约 502 / 407ms),两次串行等待约 1.13 / 0.62 秒;前端 exclusive 全程显示整屏遮罩并禁止拖动。后端事务审计确认两笔成功操作,各涉及两画布,响应约 9.5 / 9.2KB,没有图片内嵌造成的大体积。历史记录不能拆分当时网络/SQL/锁等待耗时,不能把整段耗时归因于 SQL。

改动将 members 同步改为局部状态和串行事务队列;自动保存暂缓,其他元素本地编辑继续,成功响应与持久化基线做三方合并后再保存编辑。Core PR #16 增加源画布局部坐标的 positions 字段及 member_positions 能力,坐标和归属在一个事务保存,支持时省掉前置 PATCH。版本及幂等保护保留。DOM/Hook 延迟响应、丢失响应和版本冲突场景已验证;尚未将模拟的一次 POST 当作线上耗时改善。

Core PR #16 已由用户合并为 2207c85;push #946、PR #947、主线 #949、部署 #950 均成功。前端 a240507 已在 main。23:13:30(Asia/Shanghai)开始安排指定父图 icon 拖放的真实验收;需要刷新以重新检测能力,实测结果待记录核对后补充。

首次上传及能力协商复核

23:15–23:16 的真实日志中,新 JPEG 原图 393,301 字节、1280×2276,上传约 8.5 秒;其预览 125,545 字节,首响应等待 5,913.9ms,正文传输 748.1ms,总计 6,662ms。同期 profile/workspaces/小快照也出现约 5–7 秒等待首响应,不能将全部时间归因于图片处理。随后无认证公网健康探测也复现秒级等待:curl 约 1.73/4.24 秒;Node 新建连接 DNS 20ms、TCP 累计 200.4ms、TLS 累计 3,574.6ms、总计 4,808.3ms,同连接复用请求仅 275.4/187.1ms。ECS 内部健康请求约 120.35/13.92/1.81ms。该对照表明冷连接/公网路径存在明显成本,但不是历史每个请求的同期分段追踪,不能声称已定位全部 8.5 秒或修复公网丢包。

能力协商 2 秒截止与约 1,989ms 的服务端路由耗时接近,且真实三次移动前仍有仅 position 变化的 PATCH。现移除短截止并禁止把网络错误永久缓存为“不支持”;慢响应和失败预取回归均保持原子成员移动。后端三笔事务各移动 1 个节点,总数、非位置内容与版本递增均正确。

首次上传改为本地即时预览 + 后台原图上传,服务端确认后保存引用,远端预览加载完才切换显示。该改动消除空画布等待,不将即时显示误报为网络上传已完成或服务端延迟已消失。实际交互复测待用户操作后补充。

用户随后指出只读预览不等于已添加元素。已修正为选图时就创建可编辑的本地节点,后台上传仅补齐引用;位置、尺寸、名称、删除及撤销状态都以本地最新状态为准。未准备好的文件节点和关联边不传给 Core;其他编辑仍可保存。该修正不依赖公网延迟降低,也不将本地添加误报成后台文件已上传。