跳转至

已关闭的浏览器调度器在待处理原生工作之后保留已完成结果

修复前,已完成的命令结果会一直保留在已关闭的浏览器调度器中,直到其最后一个处理器确定。该修复在关闭时释放已确定的缓存记录,并在关闭期间丢弃新确定的记录。待处理的原生处理器、页面权限和执行器清理保持其现有生命周期。

实际路径与边界

本节的源码引用描述哈希围栏基线。BrowserClientHostCommandDispatcher.dispatch 一旦关闭,便在权限或重复查找之前拒绝所有命令(browser-client-host-command-dispatcher.ts:77–79)。close 中止活动工作并取消排队工作,但当 join 返回 false 时,仍保留其页面和已缓存的已完成记录(:156–179)。finishHandler 仅在最后一个原生处理器确定后清除这些所有者(:268–272)。当兄弟项仍活动时,新确定的取消记录也会被缓存(:297–302)。

BrowserClientHostCommandResultCache.clear 仅丢弃其记录到页面索引。PageState.records 和 PageState.sequencesByCommandId 也拥有这些记录;仅清除该索引不会释放结果图。现有 releasePage 使用精确的已确定记录驱逐来移除两个索引(browser-client-host-command-result-cache.ts:27–55)。

默认值(browser-client-host-command-state.ts:7–13)为 256 个页面、256 个活动命令、8 个并发处理器、每页 32 个排队项、每页 64 个缓存结果、总共 1,024 个缓存结果,以及一个 5,000 ms 的关闭/退役 join。自动化结果模式最多允许 768 KiB 的 JSON 序列化值(browser-client-automation-protocol.ts:5,89–99,116–131)。这些是计数/序列化值限制,而非保证的堆/RSS 大小。审计使用 32 个微小结果、一个原生等待和一个已取消的微小排队输入;它不会分配接近这些最大值。

生产组合通过 PairedRuntimeBrowserClientHost.closeHost 调用调度器关闭(paired-runtime-browser-client-host.ts:165–180)。如果关闭超时,实际的 closeBrowserClientHostComposition 将执行器关闭延迟到 whenHandlersSettled 之后(paired-runtime-browser-client-host-teardown.ts:37–56)。保持处理器/页面/原生权限存活是有意为之。已完成结果无法服务新的或重复的已关闭请求,且无需共享该生命周期。

普通处理器的时间边界

  • 导航命令在开始前检查取消,然后调用 routeWebContents.navigateGuest(browser-client-page-command-execution.ts:20–40)。实际注册表委托给 navigateBrowserRouteGuest,后者等待原生 guest.loadURL(browser-route-guest-lifecycle.ts:99–123),未添加 JS 截止时间,也未获取 AbortSignal。原生完成/拒绝仍是其确定方。
  • 自动化在执行前检查取消,注册精确的 guest,并将信号转发到 RPC(browser-client-page-automation-runtime.ts:42–57;启动 main-process-ready-runtime.ts:61–77)。诸如 browser.snapshot 之类的核心处理器解构 runtime 并调用其方法,而未观察该信号(runtime/rpc/methods/browser-core.ts:39–43)。
  • 普通 agent-browser 辅助执行具有 90 秒的默认子进程超时(agent-browser-bridge-types.ts:6;agent-browser-bridge-raw-process.ts:20–36),某些操作具有覆盖。agent-bridge 内嵌 goto 包装器另有 30 秒导航超时。这些截止时间并非所有处理器阶段的通用边界,且上述直接路由导航路径不使用该包装器。

该条件是处理器存活时间超过调度器的五秒 join。尚未确认受影响主机事件、自然无限期停滞或事件归因。

有界的实际源码证明

scenario.test.mjs 使用实际的调度器、页面执行器、自动化运行时、browser.snapshot RPC 描述符、原生导航包装器和组合清理函数。现有页面执行器测试框架提供 renderer/session/route 端口。运行时的 browserSnapshot/native loadURL 是小型受控端口;未使用 Electron 窗口、操作系统子进程、真实 Web 请求或网络。日志/模拟结果数组不拥有生成的负载:自动化输出和调度器处理器是普通函数,每个已完成值仅在辅助函数返回后通过 WeakRef 观察。

Node 26.6 和 Electron 43.7 / Node 24.21 在两个文件修复前后均通过全部 4 个用例。15 ms 的 join 覆盖使证明保持有界;原生端口在 finally 中显式解决,所有原生托管最终确定。

观察项 修复前 修复后
超时关闭后仍存活的已完成小负载对象 32 0
关闭后的缓存记录(32 个结果 + create + 排队取消) 34 0
已取消的排队输入仍可达 是 否
关闭后仍在运行的原生处理器 1 1
保留页面/执行器以及路由/会话托管 是 是
在原生确定前重复关闭 否 否
在原生确定前 whenClosed 待定 是 是
显式原生确定后仍存活的负载对象 0 0
在兄弟项仍待定时缓存首个迟到取消 1 0

开放请求重放保留完全相同的 Promise 并只运行一次。已关闭的重复请求以 dispatcher_closed 失败。正常的原生解决和拒绝都完成现有确定路径。在两个变体中,执行器关闭和路由/会话释放都仅在原生确定之后发生。

初始候选兼容性运行在 5 个文件中通过 78 个测试,包括其三个生命周期用例以及现有调度器、页面执行器、paired-runtime 组合和 paired-runtime 主机测试。永久保留套件增加两个对象生命周期回归:在原生导航保持待定时释放已完成结果,以及在兄弟处理器保持活动时释放一个迟到的已关闭记录。最终验证在 5 个生产套件中通过 77 个测试、Node 类型检查,以及对产品和工件代码的全部五项显式质量扫描。反转补丁会产生两个预期的生命周期失败,同时全部 16 个现有调度器测试通过。命令和结果记录在 validation.json 中。

修复范围

fix.patch 仅修改:

  • src/main/browser/browser-client-host-command-dispatcher.ts:在关闭期间、取消之后,释放每个页面已结算的缓存;当已关闭时,丢弃新结算的记录,而不是将其缓存。
  • src/main/browser/browser-client-host-command-result-cache.ts:在 record 中接受可选的 retain 标志,默认保持现有缓存行为。当为 false 时,现有的身份检查驱逐会在任何缓存准入之前丢弃已结算记录。

活动/取消中的记录、页面、原生 Promise、中止行为、join 时序、FIFO、代际/权限检查以及关闭结算 Promise 的所有权均与之前完全一致。不更改行/字节上限。host 和 executor 继续等待其现有的结算所有者。

retirePage 是独立的

selectCommandPage 在 findExistingCommand 之前拒绝正在退役和已退役的代际(browser-client-host-command-page.ts:34–43)。因此,已退役的重复重放已经不可用,即使缓存会保留到 forget/替换。该控制项确认了该行为,并验证显式 forget 会释放缓存并保留过期代际下限。本修复保持该现有退役策略不变;在页面退役时释放结果是另一个可能的后续工作,尤其是在 executor 清理仍待处理时。不要假设存在一个实际准入路径会先拒绝的实时重复重放契约。

复现与源码围栏

ORCA_BACKGROUND_LAUNCH=1 ORCA_BROWSER_CACHE_VARIANT=before pnpm exec vitest run --config docs/audits/browser-closed-result-retention/vitest.config.mjs docs/audits/browser-closed-result-retention/scenario.test.mjs
ORCA_BACKGROUND_LAUNCH=1 ORCA_BROWSER_CACHE_VARIANT=fixed pnpm exec vitest run --config docs/audits/browser-closed-result-retention/vitest.config.mjs docs/audits/browser-closed-result-retention/scenario.test.mjs

对于 Electron,使用 ELECTRON_RUN_AS_NODE=1 和 ORCA_BACKGROUND_LAUNCH=1 运行已安装的二进制文件,并传入 node_modules/vitest/vitest.mjs 以及相同参数。报告按运行时/变体分别生成;将 ORCA_BROWSER_CACHE_OUTPUT 设置为另一个文件路径,以保留捕获的报告。sources.cjs 在内存中反转 fix.patch,并检查精确的 baseline/fixed 哈希以及 21 个调用方/依赖项哈希。该配置按真实生产模块 ID 加载这些源码,而不更改检出文件。一个合成 CRLF 控制项针对所有 24 个源码/补丁读取检查规范 LF 哈希。两个变体使用相同的受控生产者和生命周期端口。

source-versions.json 记录 23 个规范 LF 源码/调用方哈希。全部 23 个匹配命名 main 检查点 291b4ddd6f1c1af480169885e0fda7f9c78ff053;21 个匹配 v1.4.198。两个修复后的源码基线均匹配两个命名版本。该证明执行当前源码/依赖项,而不是历史应用二进制文件。

永久回归验证

ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config config/vitest.config.ts src/main/browser/browser-client-host-command-retention.test.ts src/main/browser/browser-client-host-command-dispatcher.test.ts src/main/browser/browser-client-page-command-executor.test.ts src/main/browser/paired-runtime-browser-client-host-composition.test.ts src/main/browser/paired-runtime-browser-client-host.test.ts
ORCA_BACKGROUND_LAUNCH=1 ORCA_BROWSER_CACHE_VARIANT=before pnpm exec vitest run --config docs/audits/browser-closed-result-retention/vitest.config.mjs src/main/browser/browser-client-host-command-retention.test.ts src/main/browser/browser-client-host-command-dispatcher.test.ts
ORCA_BACKGROUND_LAUNCH=1 pnpm tc:node

基线回归命令有意使两个新的生命周期断言失败。源码数量和对象数量证明的是一种代码机制,而不是特定事件的浏览器使用、原生停滞时长、应用总 RSS,或归因于 #19831。

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