Skip to content

[DRAFT] 🐛 异步 GM.getValue/getValues/listValues 确保读取最新 values(#950 重设计)#1626

Draft
cyfung1031 wants to merge 1 commit into
scriptscat:mainfrom
cyfung1031:fix/async-gm-value-freshness
Draft

[DRAFT] 🐛 异步 GM.getValue/getValues/listValues 确保读取最新 values(#950 重设计)#1626
cyfung1031 wants to merge 1 commit into
scriptscat:mainfrom
cyfung1031:fix/async-gm-value-freshness

Conversation

@cyfung1031

@cyfung1031 cyfung1031 commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Checklist / 检查清单

  • Fixes 异步 GM.getValue/getValues/listValues 可能读到过期本地缓存的问题
  • Code reviewed by human / 代码通过人工检查
  • Changes tested / 已完成测试

背景

#950 的想法在当前 main 分支代码上重新设计。原问题(#898 / #950):页面端 GM values 走本地缓存,其他 tab / 环境写入后经 valueUpdate 异步推送同步,异步的 GM.getValue / GM.getValues / GM.listValues 在推送到达前读取会返回过期值,且与 valueUpdate 竞争时列表次序可能错乱。

#950 基于 release/v1.3 的旧推送通道(chrome.tabs.sendMessage 逐 tab 推送),包含按 storageName 批量合并任务等大规模重构。当前 main 已改为 chrome.storage.sessionvalueUpdateDelivery)单通道广播并经 runtime.pushValueUpdate 统一推送,原 diff 无法套用;本 PR 保留其核心思路,以最小侵入方式落在现行架构上。

本次改动

  • ValueUpdateDataEncoded 新增 updatetime 字段:valueDAO 中该 storageName 当前的更新时间戳,随每条 valueUpdate 推送到各执行环境。
  • ValueService.setValues:值变更时以 aNow()(严格递增)刷新 valueModel.updatetime 并随推送携带;值未变更时也携带当前 updatetime
  • 新增 ValueService.waitForFreshValueStateid 非空时先执行一次空 setValues,借同一条 valueUpdate 推送通道把当前 updatetime(连同 id)送达调用方页面;返回 valueDAO 当前 updatetime(读取经同一 stackAsyncTask 队列,避免读到写入中途状态)。
  • service_worker GMApi 新增内部 API internalApiWaitForFreshValueStatedefault: true,不对应任何 @grant)。
  • 页面端 GM.getValue / GM.getValues / GM.listValues 读取前先经 waitForFreshValueState 同步:
    • 尚未收过任何 valueUpdatevalueDaoUpdatetimeundefined)时,附带 id 触发上述空 setValues 回推;
    • service_worker 返回的 updatetime 比本环境已同步时点新时,登记 readFreshes 等待对应 valueUpdate 到达后才读取本地缓存。
    • 同步 API(GM_getValue 等)行为不变。
  • 顺带把 _GM_setValue / _GM_setValues 重复的 id 生成抽为 generateValChangeIdwaitForFreshValueState 也需要)。

实现考虑

  • 新鲜度不变量updatetime 每次变更严格递增(aNow()),客户端只需等待「收到 updatetime >= 服务端返回值valueUpdate」即可确认本地缓存不旧于调用时点的 DAO 状态;readFreshesupdatetime >= t 批量放行,推送合并/跳变也不会悬挂。
  • 通道复用:同步信号走既有 valueUpdate 推送通道(storage.session 广播),天然与值变更推送保持全序,无需新增消息类型或改动 runtime.pushValueUpdate / sandbox / offscreen 转发链。
  • 失效兜底message 不可用或 extension context invalidated 时(sendMessage 返回 undefined),清理挂起的 id 回调并直接读本地缓存,不会永久挂起。
  • 未采用 [Dev] 异步 getValue/getValues/listValues 相关修改 - 确保 GM.getValue 取最新值(优先度:低) (PR-TAG: TBC) #950 的按 storageName 批量合并写入(valueUpdateTasks / ValueUpdateSendData):那是性能与旧通道次序问题的重构,当前 main 的 stackAsyncTask(cacheKey) 序列化已保证写入与推送次序,为控制风险与 diff 范围不搬入。

已知限制

  • 首次异步读取需一次 service_worker 往返(空 setValues + 回推),之后仅在检测到落后时才等待;这是「保证读到最新值」的固有成本。
  • valueUpdate 推送本身丢失(delivery 通道异常),等待方会依赖后续任意一次推送放行;推送可靠性问题不在本 PR 范围。

建议审查重点

  • waitForFreshValueState(content 端)在并发调用、context 失效、updatetime === 0(无 value model)各路径下不悬挂、不早读。
  • setValuesupdatetime 在「新建 / 变更 / 未变更」三分支的取值。
  • internalApiWaitForFreshValueState 使用 default: true 的权限面:仅回推本脚本自身 storage 的时间戳与空 entries,不泄露值内容。

关联

验证

  • pnpm run test:ci:309 files / 3440 tests 全部通过。
  • pnpm run lint(prettier + tsc + check:i18n + eslint):通过。
  • TDD:先提交失败测试(9 failed)再实现;新增覆盖:
    • content:异步 GM.getValue/getValues/listValues 会发出 internalApiWaitForFreshValueState 并等待回推;updatetime 落后时在对应 valueUpdate 到达前不完成、到达后读到新值;message 无效时直接返回本地缓存。
    • service_worker:setValues 刷新并携带 updatetime(含未变更分支);waitForFreshValueState 带/不带 id 的行为。
  • 未做真实浏览器端到端验证(未构建扩展实测多 tab 场景),如需可按 docs/verification.md 补充。

🤖 Generated with Claude Code

重新设计自 scriptscat#950:valueDAO 在每次值变更时以严格递增的
updatetime 记录版本,valueUpdate 推送携带该 updatetime;页面端异步 GM.*
读取前经 internalApiWaitForFreshValueState 取得当前 updatetime,未同步时
等待对应 valueUpdate 到达后才读取本地缓存,避免读到过期值或次序错乱。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cyfung1031 cyfung1031 changed the title 🐛 异步 GM.getValue/getValues/listValues 确保读取最新 values(#950 重设计) [DRAFT] 🐛 异步 GM.getValue/getValues/listValues 确保读取最新 values(#950 重设计) Jul 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant