跳转至

已完成的 RPC 队列记录仍保持可达

状态:在 Node 26 以及以 Node 模式安装的 Electron 43.7.0 上,产品修复前后均复现。

RuntimeRpcCallQueuePool 推进每个通道的头部,但不清除已消费的数组元素。只要有任何调用使该选择器保持活动,已完成记录会保持其 run、resolve、reject 和 signal 字段可达,直到同一通道执行压缩。压缩要求已消费条目超过 32 个,并且至少一半数组已被消费。当所有活动和排队调用都结束时,删除选择器也会释放这些数组。

该修复在 takeForeground 和 takeBackground 中将已消费槽位赋值为 undefined,并允许这两种数组元素类型包含 undefined。活动调用仍由其 Promise 清理闭包持有。排队条目、两个通道头部、计数、准入阈值、批处理、前台优先以及取消行为均保持不变。

生产可达性

  • 桌面端 src/main/ipc/runtime-environment-call-queue.ts 拥有一个模块单例。runtime-environment-transport-routing.ts::callRuntimeEnvironment 传入一个闭包,捕获请求参数、环境和可选的编排信封。其正常超时为 15 秒;status.get 绕过此队列。
  • 配对 Web 端 src/renderer/src/web/preload-api/web-runtime-session.ts 拥有另一个模块单例。web-runtime-calls.ts 和 web-filesystem-api.ts::captureWebFileMutationSession 传入闭包,捕获参数、环境,有时还捕获显式客户端。Web 请求超时在连接就绪后默认为 30 秒。
  • 当前生产调用方将 retainedBytes 传入/默认为零。证明中的非零字节值用于验证队列的记账;它们并不能证明已部署调用方在此处对其对象图进行记账。
  • 这些是远程/配对执行路径。该发现仅凭代码可达性无法解释仅限本地的 #19831。

单个调用时长有限,并不能保证旧通道的已消费记录会消失:另一通道中的重叠调用可能使选择器保持活动。扩展证明在修复前完成 70 次连续后台调用,同时八个已完成的前台负载保持可达;每个后台调用都会结束,释放最后一个调用会使选择器空闲并允许回收。

这是超出有效生命周期的保留,其记录数量受现有压缩/准入行为限制。它不能证明单个选择器会无限增长,也不能证明任何已报告事件的影响规模。隔离的负载是有界的假数组,而不是真实网络负载。

证明

reproduce.cjs 打包了实际队列模块及其实际导入。queue-source.cjs 通过反转 fix.patch 在内存中重建基线;基线和当前源码哈希都必须与 source-versions.json 匹配。八个已完成调用捕获八个 1 MiB 类型化数组负载,另有另一个调用使选择器保持活动。弱引用在修复前保持存活,修复后全部清除。前台和后台通道、最终压缩以及最终空闲清理均已覆盖。在陈旧保留期间,队列字节额度和排队调用计数已经等于零。每个测试夹具中故意保持存活的负载最多为 8 MiB;进程具有 128 MiB 旧空间限制和十秒截止时间。

extended-controls.cjs 检查活动负载在其调用 settle 之前被保留、取消会释放排队负载而无需等待无关的活动调用、同步失败在修复前仅在压缩/空闲后释放、滚动跨通道流量,以及包含六次取消的 140 次调用混合突发。两个变体都以 FIFO 通道顺序执行相同的 134 个剩余调用,并带有前台优先,然后删除空闲选择器。

resolver-controls.cjs 在不使用队列的情况下隔离运行时的已 settle Promise 解析器行为。保留原生 resolve 函数在某些运行时版本中也可能使已 settle 的结果保持可达;这必须与输入闭包分开报告。

Node 和 Electron 结果分开存储。Node 26.6.0/V8 14.6 即使存在陈旧队列记录,也会回收新的响应负载。已安装的 Electron 43.7.0/Node 24.21.0/V8 15.0 在修复前保留全部八个新的响应负载,并在修复后释放它们。独立解析器控制复现了相同差异:保存原生 resolve 函数会在 Electron 中保留八个负载中的八个,在 Node 中保留零个;释放这些函数后两者都允许回收。该控制隔离了运行时 Promise 行为,并不声称所有 Electron 版本或浏览器渲染器模式行为相同。

原始仅限 Node 的响应保留负向控制预期在 Electron 下失败。基线现在记录该结果,而不是假设每个 V8 版本都以相同方式释放已 settle 的结果。修复后的变体必须在两种环境中都释放响应。该证明没有测量确切的 Electron 43.4.1 历史二进制文件。

从工作树运行:

ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=128 docs/audits/runtime-rpc-consumed-queue/reproduce.cjs
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=128 docs/audits/runtime-rpc-consumed-queue/extended-controls.cjs
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=128 docs/audits/runtime-rpc-consumed-queue/resolver-controls.cjs
ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/shared/runtime-rpc-call-queue.test.ts src/shared/runtime-rpc-call-queue-retention.test.ts

已安装的 Electron 二进制文件可以使用 ELECTRON_RUN_AS_NODE=1、ORCA_BACKGROUND_LAUNCH=1 和相同的 Node 标志运行相同脚本。不会创建 Electron 应用或窗口。不使用网络、麦克风或受影响主机数据。本会话未暴露堆快照工具;证明测量 WeakRef 可达性和有界进程计数器,而不是读取原始堆快照。

现有八个队列测试和六个新增的保留/生命周期测试在修复后通过。要在重建的基线上复现三个保留失败,请使用 ORCA_BACKGROUND_LAUNCH=1 node --expose-gc node_modules/vitest/vitest.mjs run --config docs/audits/runtime-rpc-consumed-queue/baseline.config.mjs src/shared/runtime-rpc-call-queue.test.ts src/shared/runtime-rpc-call-queue-retention.test.ts;预期结果为 11 个测试通过和 3 个失败,退出码为 1。Node 和 Web 项目的类型检查以及变更代码质量门禁在晋级期间通过。显式的基础/类型感知 lint 也覆盖这些否则会被忽略的审计脚本。

来源标识

修复前的队列源码、获取的 origin/main 以及 v1.4.198 在审查时均具有 SHA-256 45921658a10ffeefe0247673227aab643d1b5251d24aa460244fadd5480fc349。source-versions.json 记录了确切的基线/当前哈希以及所比较的提交 ID。该发布版本中的可用性支持可达性分析;它不用于事故归因。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 orca48/orca