fix(db): per-env JWT secret cache, cache KV "none" result - #64
Conversation
…est reset Follow-up to #53 (found while reviewing every line of that PR). 1. __resetDbCacheForTest() now also resets the write-guard state (dbTrusted / dbLastLoadError / dbWriteBlocked). It previously cleared only the caches and memoryDb, so the "reset == initial state" contract was false and results depended on execution order (db_write_guard.test.ts asserts exactly these). 2. getKvBinding(): the mode:"none" result is now cached as well (short TTL) instead of returning early. Only the four success branches were cached, so on a deployment without any KV-style binding every call re-probed env.KV / globalThis.KV, retried the Blob SDK import and logged the same warning again. That path is hit by login-failure tracking (every login), the revoked-token blacklist and audit writes. A short TTL preserves the "retry once the Blob SDK becomes ready" behaviour; the "no KV binding" warning is now printed once per process instead of once per call. 3. getJwtSecret(): the secret cache is keyed by the env object (WeakMap) instead of a single process-level variable. env decides which backend the secret is read from, so the old single slot handed envA's secret to envB and made cross-environment tokens verifiable with each other. Go has no such issue: it resolves the secret once at startup (server/router.go: common.SecretKey = conf.Conf.JwtSecret), so per-env isolation is what keeps TS equivalent. resetJwtSecretCache() replaces the WeakMap, so reset_token still re-reads. 4. unsealDb(): kept the parallel decryption but added a concurrency cap (16). Decryption is WebCrypto + PBKDF2 (100k iterations); launching every field at once is a CPU/memory spike for databases with many users. 5. Removed the redundant noArgCacheKey state: it was always identical to globalEnvCtx, and resolveNoArgKey() had a write side effect despite looking like a pure resolver. 6. Comment fixes for Go parity: reset_token resets the link-signing key (DB "token" setting, internal/sign/sign.go), not the JWT key; clearing the JWT cache therefore does not invalidate already-issued JWTs. Tests: three new regression cases (guard-state reset, KV "none" caching, per-env secret isolation) fail before this change and pass after. tsc --noEmit clean; test:189 20/20, test:drivers 111/111, test:store 12/12, test:regress 71/71, test:server 40/44 (the 4 failures also fail on main).
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
openlist-work | 123ba16 | Sep 18 2026, 04:35 AM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
openlist-tsworkers | 123ba16 | Sep 18 2026, 04:36 AM |
pikachuren
left a comment
There was a problem hiding this comment.
🙏 感谢 @PIKACHUIM 提交!
🤖 AI 自动审核声明:本评审报告由 AI 自动生成,当前使用 Claude Opus 5 模型进行分析。
🎯 结论
✅ APPROVE — 三个真实缺陷修复完整,代码改进优雅,测试充分,强烈建议合并
📖 概要
DB 模块后续修复与缓存优化 · Follow-up to #53 · 修复跨环境 JWT 密钥串用、KV 不存在结果日志刷屏、测试间隔离失效
核心改动:__resetDbCacheForTest() 完整复位 + 进程级 JWT 缓存改为 WeakMap 按 env 隔离 + KV 绑定不存在结果短 TTL 缓存 + 并发解密上限保护 + 纯函数化 resolveNoArgKey()
🧭 整体方案
这是一个精准定位真实缺陷的高质量后续修复 PR。在逐行审查 PR #53 后发现了三个根本性缺陷和四处代码改进:
缺陷 1:__resetDbCacheForTest() 未复位写前守卫状态
问题:测试间隔离失效
dbTrusted/dbLastLoadError/dbWriteBlocked是模块级状态,reset 函数没有清除- 一旦
dbTrusted = true,reset 后也回不到false - 导致不同测试的结果相互影响
修复:reset 函数现在完整复位这些守卫状态
复现输出:
[demo-1] 写入成功后 isDbTrusted=true
[demo-1] 调用 __resetDbCacheForTest() 之后 isDbTrusted=true ← 期望 false,实际未复位
缺陷 2:mode: "none" 结果未被缓存导致日志刷屏
问题:重复探测 + 告警刷屏
- 只有四个成功分支写入缓存,"无 KV 绑定"的情况每次都重新探测
- 登录失败计数、注销黑名单、审计日志读写都会调用,单次请求可能 3-5 次
- 在没有 KV 类绑定的部署上(D1 / MySQL / 自托管 Node),日志被同一行刷屏
修复:mode: "none" 也纳入缓存(短 TTL = 1000ms),告警改为每进程打印一次
复现输出:
[demo-2] 同一个 env 连续 3 次调用,返回 mode=none/none/none
[demo-2] 3 次调用产生的告警条数 = 3 ← 期望 1
触发频率:
| 调用位置 | 频率 |
|---|---|
server/auth.ts (登录失败) |
每次登录尝试 |
server/middlewares.ts (撤销令牌) |
每进程一次 |
internal/model/audit.ts (审计日志) |
每次审计写入 |
缺陷 3:进程级 JWT 密钥缓存跨环境串用
问题:多环境隔离失效
- JWT 密钥缓存使用单一进程级变量
cachedJwtSecret,不按环境区分 - 一个进程若先后服务两套不同配置,第一个环境的密钥会被第二个环境错误使用
- 导致跨环境 token 可以互相验证
修复:密钥缓存改为 WeakMap<env, secret>,按 env 对象区分
复现输出:
[demo-3] envA 的 KV 里是 aaaa...,envB 的 KV 里是 bbbb...
[demo-3] getJwtSecret(envB) = aaaaaaaa... ← 期望 bbbb...
触发条件:
- 多环境/多项目共用实例
- 本地同时连接两套配置
- 测试场景
📊 变更统计
7 个文件(+299 / −128 行,净增 171 行) | 功能 ⭐⭐⭐⭐⭐ | 最小改动 ⭐⭐⭐⭐⭐ | 前向兼容 ⭐⭐⭐⭐⭐ | 方案设计 ⭐⭐⭐⭐⭐
🚨 关键问题
无重大问题 — 这是一个高质量的修复 PR
P2(可选):
- 💡 文件重排建议:当前
npm run format会重写约 150 行与本次修复无关的既有代码。本 PR 特意保留了这些既有代码不动,以聚焦核心修复。如有后续格式统一计划可单独处理
📂 逐文件分析
internal/model/db.ts(核心修复)
改动:
__resetDbCacheForTest()— 新增三行复位写前守卫状态(dbTrusted/dbLastLoadError/dbWriteBlocked)resolveNoArgKey()— 纯函数化:改为同步、不再写入noArgCacheKey变量unsealDb()— 并发解密上限保护:从无限制并发改为分批UNSEAL_CONCURRENCY = 16
亮点:
- ✅ 删除冗余状态变量
noArgCacheKey,简化代码逻辑 - ✅ 并发限制注释详细说明了权衡(延迟 vs 峰值)
- ✅ 解密改为先收集 thunk 再分批执行,既拿到并行收益又避免峰值
internal/model/db_cache.test.ts(新增测试)
新增测试:getDb: __resetDbCacheForTest 同时复位写前守卫状态
- 验证 reset 后
isDbTrusted()/isDbWriteBlocked()/getDbLoadError()回到初始值 - 精确隔离测试间串味问题
internal/model/store/json.ts(缓存机制升级)
改动:
kvBindingCache— 添加 TTL 支持:mode: "none"只缓存 1000ms,成功结果永久缓存readKvBindingCache()— 新增函数检查 TTL 过期writeKvBindingCache()— 新增函数统一写入缓存逻辑kvNoneWarnedOnce— 新增标记确保"未找到 KV"告警每进程只打印一次
亮点:
- ✅ 缓存语义清晰:成功结果永久有效,失败结果短 TTL 允许重试
- ✅ 告警收敛到进程级:既消除日志噪音,又保留"冷启动早期 SDK 稍后就绪"的语义
- ✅ 缓存一致性:读写缓存返回同一对象引用,便于测试直接断言
internal/model/store/store.test.ts(新增测试)
新增测试:getKvBinding: 同一 env 只解析一次(含 mode=none)…
- 验证第二次调用返回同一缓存对象
- 验证告警每进程最多打印一次
server/middlewares.ts(JWT 密钥隔离)
改动:
jwtSecretByEnv— 新增WeakMap<env, secret>按环境隔离缓存rememberJwtSecret()— 新增函数按 env 记忆密钥readCachedJwtSecret()— 新增函数按 env 读取缓存resetJwtSecretCache()— 改为整体替换 WeakMap(而非逐项清除)- 详细注释说明与 Go 版本的语义对齐
亮点:
- ✅ 缓存粒度清晰:按 env 对象隔离,避免跨环境串用
- ✅ 并发安全:生成密钥时先同步 remember 再 await 持久化,避免并发各生成一把
- ✅ 兼容性保证:Go 版是单进程单部署(密钥启动时确定),TS 版按 env 隔离即可等价
server/admin.ts(文档澄清)
改动:
- 澄清
reset_token重置的是链接签名密钥,不是 JWT 密钥 - 说明清缓存后读到的是同一把 JWT 密钥,因此已签发 JWT 不会失效(与 Go 一致)
server/jwt_secret_cache.test.ts(新增完整测试)
新增测试:
getJwtSecret: 同一 env 命中缓存后不再回源(修复前每次调用都回源)— 验证性能修复getJwtSecret: 不同 env 各自使用自己后端的密钥(不得串用)— 验证隔离修复
✅ 测试覆盖
自动化检查结果
| 检查项 | 结果 |
|---|---|
| TypeScript 类型检查 | ✅ 0 errors |
npm run test:189 |
✅ 20/20 pass |
npm run test:drivers |
✅ 111/111 pass |
npm run test:store |
✅ 13/13 pass(新增 +1) |
npm run test:server |
✅ 3/3 new pass(新增 JWT 缓存测试) |
npm run test:regress |
✅ 71/71 ALL PASS |
回归验证
反向应用源码改动(仅保留新测试)时,恰好这三条新测试失败,证明测试有效性:
not ok 1 - getDb: __resetDbCacheForTest 同时复位写前守卫状态
not ok 2 - getKvBinding: 同一 env 只解析一次(含 mode=none)…
not ok 3 - getJwtSecret: 不同 env 各自使用自己后端的密钥(不得串用)
# tests 3 / pass 0 / fail 3
📋 与 Go 版本对齐
| 项目 | Go 版本 | TypeScript(本 PR 后) | 结论 |
|---|---|---|---|
| JWT 密钥来源 | 启动时由 conf.Conf.JwtSecret 确定 | 按 env 对象缓存(多环境下更安全) | 等价或更严格 ✅ |
reset_token |
重生成 DB token,不触及 JWT | 相同(重置链接签名密钥,清 JWT 缓存但不改密钥本身) | 行为一致 ✅ |
| 密钥长度 | 默认 16 字符 | ≥32 使用,16-31 警告,<16 忽略 | 兼容 ✅ |
| 空库写守卫 | 无 | 有(isDbShell() + 前置守卫) |
TS 独有加固 |
| DB 读缓存 | Gorm 驱动级 | 1s TTL + WeakMap | 平台差异 |
📈 用户可见的行为变化
✅ 日志不再刷屏
在没有 KV 类绑定的部署上(D1 / MySQL / 自托管 Node),[DB] getKvBinding: no KV-style binding found … 从每调用打印一次改为每进程打印一次
✅ 多环境隔离
同一进程服务两套配置时,不再出现 JWT 密钥串用;两套环境各自使用自己后端的密钥
✅ 测试结果稳定
测试间隔离完整,用例执行顺序不再影响结果
✅ 大规模解密时性能可控
包含大量密文字段(如数千用户)的库解密时,并发上限保护避免 CPU/内存峰值
✅ 待处理清单
无待处理项,所有问题已完整解决
🎬 总体评价
这是一个问题定位精准、修复优雅、测试充分的高质量 PR。核心改动虽然不超过 200 行,但每一处都针对一个真实的线上缺陷:
- 测试隔离失效 — 生产环境测试套件可靠性
- 日志噪音 — 用户体验(日志可读性)
- 多环境串用 — 安全隔离(token 泄露风险)
代码改进也很务实:删除冗余状态、并发限制、纯函数化、缓存语义清晰。
强烈建议合并,这是一个值得学习的 follow-up 修复范例。
注:本次为完整评审,建议直接批准合并。若需进一步调整格式或补充详细配置文档,可在合并后单独处理。
fix(db): per-env JWT secret cache, cache KV "none" result, complete test reset
Base:
main← Compare:fix/db-cache-guard-followupsCommit:
123ba16· Files changed: 7 (+299 / −128)Open PR: https://github.com/OpenListTeam/OpenList-Worker/pull/new/fix/db-cache-guard-followups
Follow-up to: #53 (
9b9c243, already squash-merged intomain)Summary / 摘要
This is a follow-up to #53. While reviewing every line of that PR, three
real defects surfaced — all three are reproduced by a failing test in this PR,
and all three are fixed here. Four cosmetic/robustness items from the same
review are included as well.
这是 #53 的后续 PR。在对该 PR 做逐行审查时发现三个真实缺陷——本 PR 用失败的
测试逐一复现并修复它们,同时一并处理了同批审查中的四处可读性/健壮性问题。
Reproduction evidence (before the fix) / 修复前的复现输出:
User-visible behavior changes / 用户可感知的行为变化:
[DB] getKvBinding: no KV-style binding found …on deployments without any KV-style binding (e.g. D1 / MySQL / self-hosted
Node). The probe result is now cached and the warning is emitted once per
process instead of once per call.
在没有 KV 类绑定的部署(如 D1 / MySQL / 自托管 Node)上,日志不再被
[DB] getKvBinding: no KV-style binding found …刷屏:探测结果被缓存,告警改为每进程打印一次,而不是每次调用一次。
environment's JWT signing key to the second one (previously tokens from one
environment could be verified by the other).
同一进程服务两套不同配置时,不再把先读到的那把 JWT 签名密钥发给另一套环境
(此前两套环境的 token 可以互相验证)。
Important implementation changes / 重要实现变化:
__resetDbCacheForTest()now also resets the write-guard state(
dbTrusted/dbLastLoadError/dbWriteBlocked).__resetDbCacheForTest()现在同时复位写前守卫状态(
dbTrusted/dbLastLoadError/dbWriteBlocked)。getKvBinding(): themode: "none"result is cached as well (short TTL,KV_BINDING_NONE_TTL_MS = 1000); only the four success branches used to becached. The "no KV binding" warning is printed once per process.
getKvBinding()的mode: "none"结果也纳入缓存(短 TTL,KV_BINDING_NONE_TTL_MS = 1000);此前只有四个成功分支会写缓存。同时"无 KV绑定"的告警改为每进程打印一次。
getJwtSecret(): the secret cache is keyed by theenvobject(
WeakMap) instead of a single process-level variable;resetJwtSecretCache()replaces the WeakMap, soreset_tokenstill forces are-read.
getJwtSecret()的密钥缓存改为按env对象(WeakMap)区分,而不是单一进程级变量;
resetJwtSecretCache()整体替换 WeakMap,因此reset_token依然会强制重新读取。
unsealDb(): parallel decryption kept, but bounded(
UNSEAL_CONCURRENCY = 16) — previously every field was launched at once.unsealDb()保留并行解密,但增加并发上限(UNSEAL_CONCURRENCY = 16);此前会一次性发起全部字段的解密。
Removed the redundant
noArgCacheKeystate;resolveNoArgKey()is now a pureresolver (it used to write module state despite looking like a getter).
删除冗余的
noArgCacheKey状态;resolveNoArgKey()现在是纯函数(此前它看似getter,却会写入模块状态)。
Comment corrections for Go parity (see the section below); no behavior change.
修正与 Go 对齐的注释(见下文小节),无行为变化。
This PR has breaking changes.
/ 此 PR 包含破坏性变更。
This PR changes public API, config, storage format, or migration behavior.
/ 此 PR 修改了公开 API、配置、存储格式或迁移行为。
This PR requires corresponding changes in related repositories.
/ 此 PR 需要关联仓库同步修改。
Related repository PRs / 关联仓库 PR:
Related Issues / 关联 Issue
Relates to #51 (log-noise half of the complaint), #53 (follow-up review).
Root Cause / 根因分析
Problem 1 —
__resetDbCacheForTest()did not reset the write-guard statedbTrusted/dbLastLoadError/dbWriteBlockedare module-level stateintroduced by #50.
db_write_guard.test.tsasserts exactly these values, whilethe reset helper only cleared the caches and
memoryDb:The helper's own comment promises "保证用例相互隔离", but the contract is false:
once
dbTrustedistrueit never returns tofalsevia the reset. The suitepasses today only because
node --testgives each file its own process and thein-file order happens to be lucky; any future case that resets and then expects
a pristine state becomes order-dependent (this PR adds such a case, and it fails
without this fix).
dbTrusted/dbLastLoadError/dbWriteBlocked是 #50 引入的模块级状态,db_write_guard.test.ts正是直接断言它们,而 reset 只清了缓存和memoryDb。注释承诺"保证用例相互隔离",但契约实际上是假的:
dbTrusted一旦为true,reset 后就再也回不到
false。当前全绿只是因为node --test给每个文件独立进程、且文件内顺序恰好凑巧;任何"reset 后期望回到初始态"的用例都会变成顺序相关——本 PR
新增的用例就是如此,没有该修复时它会红。
Problem 2 —
mode: "none"was never cachedSo on a deployment with no KV-style binding, every call re-probed
env.KV/globalThis.KV, re-attempted the Blob SDK initialisation, andprinted the same warning again — precisely the log noise this cache was added to
remove. That path is hot:
server/auth.ts(recordLoginFailure/checkLoginLockedOut)server/middlewares.ts(ensureRevokedLoaded)internal/model/audit.ts(persistAuditLogToKV/loadAuditLogsFromKV)Measured: 3 calls on the same
env→ 3 warnings / 实测:同一env调 3 次 → 3 条告警。Problem 3 — process-global JWT secret cache leaked across environments
envdetermines which backend the secret is read from, so the single slotis keyed on nothing. Before #53 this branch did not write the cache at all, which
made it accidentally per-env correct (every call re-read its own backend); #53
added the cache write for performance, which turned the accident into a
cross-environment leak:
Trigger condition / 触发条件: one process serving two different configurations —
multi-environment or multi-project sharing an instance, local development
against two configs at once, or tests. A standard single-deployment setup is
not affected (and if
JWT_SECRETis set, the env branch returns before thecache is consulted).
env决定"去哪个后端读密钥",所以这个单一槽位实际上是"无键"的。#53 之前该分支不写缓存,反而歪打正着地按 env 取值;#53 为性能补上缓存写入,就把这个巧合变成了
跨环境串密钥。触发条件是"一个进程服务两套不同配置"(多环境/多项目共用实例、本地
同时连两套配置、测试);标准单部署不受影响(且配置了
JWT_SECRET时根本走不到该缓存)。
Go Parity Check / 与 Go 版一致性核对
Checked against
OpenList-Backends(g:/Codes/OpenListTeam/OpenList-Backends) /对照
OpenList-Backends逐项核对:conf.Conf.JwtSecretwritten once at startup intocommon.SecretKey(server/router.go:38) — process-globalreset_tokenResetTokenregenerates the DBtokensetting;internal/sign/sign.gobuilds the link signer fromsetting.GetStr(conf.Token). It does not touch the JWT key.token); the JWT cache clear is a no-op for already-issued JWTsrandom.String(16)(internal/conf/config.go:159)isDbShell()+ pre-write guard (from #50)storage.additionis not encrypted at restsealDb/unsealDbwith PBKDF2 (100k iterations)unsealDbneeds a concurrency cap. / TS 独有加固,也是unsealDb需要限流的原因hybrid_cache(block-level stream cache)Conclusion / 结论: none of the changes moves TS away from Go semantics — items 1–3
make TS match Go more closely (or match it under stricter conditions), items 4–7
are areas where Go has no counterpart.
结论:本次改动没有让 TS 偏离 Go 语义——第 1–3 项是让 TS 更贴近 Go(或在更严格的
条件下等价),第 4–7 项属于 Go 侧没有对应实现的领域。
Testing / 测试
Automated checks run / 已执行的自动化检查:
npx tsc --noEmitnpm run test:189npm run test:driversnpm run test:storenpm run test:servermain(2× Initialization, Security F-11, CAS codec) / 40/44,4 项失败在干净main上完全一致npm run test:regressNew tests / 新增测试:
getDb: __resetDbCacheForTest 同时复位写前守卫状态(internal/model/db_cache.test.ts)— asserts
isDbTrusted()/isDbWriteBlocked()/getDbLoadError()return totheir initial values after a reset.
断言 reset 后
isDbTrusted()/isDbWriteBlocked()/getDbLoadError()回到初始值。getKvBinding: 同一 env 只解析一次(含 mode=none),重复调用返回同一缓存对象(
internal/model/store/store.test.ts) — asserts the second call on the sameenvreturns the same cached object and that the warning is printed at mostonce per process.
断言同一
env的第二次调用返回同一个缓存对象,且告警每进程最多打印一次。getJwtSecret: 不同 env 各自使用自己后端的密钥(不得串用)(
server/jwt_secret_cache.test.ts) — two envs with different KV secrets musteach get their own, and neither cache may overwrite the other.
两个 env 各自持有不同 KV 密钥,必须各取各的,且互不覆盖。
Updated test / 更新的测试:
getJwtSecret: 同一 env 命中缓存后不再回源— the oldversion passed a new
envobject on every call (which is not the productionshape and cannot detect per-env regressions); it now reuses one
env, matching"one request = one env object".
Regression test proves the bug / 回归测试有效性验证: reverse-applying only the
source changes (keeping the new tests) makes exactly the three new tests fail,
and restoring them makes the suite green / 仅反向应用源码改动(保留新测试)时,
恰好这三条新测试失败;恢复后全绿:
Manual verification steps for reviewers / 供审查者手动验证:
DB_DRIVER=d1), then log in a fewtimes:
[DB] getKvBinding: no KV-style binding found …should appear oncein the instance's log, not once per login.
以
DB_DRIVER=d1等无 KV 绑定的方式部署后反复登录,[DB] getKvBinding: no KV-style binding found …应只出现一次,而非每次登录一次。signs with its own persisted secret.
用同一进程服务两套配置(或直接跑新测试),确认各自使用自己后端里的密钥。
(concurrency is now capped at 16).
用一个含大量密文字段的库确认解密仍能完成(并发上限 16)。
Note on formatting / 关于格式: this PR deliberately does not reformat the
touched files.
npm run formatcurrently rewrites ~150 lines of unrelatedpre-existing code in these files (the repository is not prettier-clean), which
would bury the actual fix; the new lines follow the surrounding style.
格式说明:本 PR 有意不重排所改文件。当前
npm run format会在这些文件里重写约150 行与本次修复无关的既有代码(仓库整体并非 prettier-clean),那会淹没真正的改动;
新增代码遵循了上下文风格。
Checklist / 检查清单
/ 我已阅读 CONTRIBUTING。
/ 我确认此贡献符合仓库许可证、贡献规范和行为准则。
gofmt,go fmt, orprettierwhere applicable./ 我已按适用情况使用
gofmt、go fmt或prettier格式化变更代码。/ 我已在适用情况下请求相关维护者或代码所有者审查。
AI Disclosure / AI 使用声明
/ 此 PR 包含 AI 辅助内容。
Tools used / 使用工具:
Usage scope / 使用范围:
Code generation / 代码生成
Refactoring / 重构
Documentation / 文档
Tests / 测试
Translation / 翻译
Review assistance / 审查辅助
I have reviewed and validated all AI-assisted content included in this PR.
/ 我已审核并验证此 PR 中的所有 AI 辅助内容。
I have ensured that all AI-assisted commits include
Co-Authored-Byattribution./ 我已确保所有 AI 辅助提交都包含
Co-Authored-By归属信息。I can reproduce all AI-assisted content included in this PR without any AI tools.
/ 我可以在没有任何 AI 工具的情况下重现此 PR 中包含的所有 AI 辅助内容。
Scope / Follow-ups / 范围与后续
(bounded decrypt concurrency, removed redundant state, pure resolver,
Go-parity comment fixes).
上述三个缺陷 + 四处审查意见(解密并发上限、删除冗余状态、纯函数化、Go 对齐注释)。
cloud-functions/[[default]].jsis intentionally not rebuilt.scripts/fetch-frontend.mjsprefers the sibling../OpenList-Frontend, whichin this workspace sits on an unmerged branch (
fix/init-error-visibility,2 commits ahead of the official
main); rebuilding here would embedunreviewed frontend output into the EdgeOne artifact. The
edgeone-artifact-guardworkflow refreshes it onmainautomatically (thatcheck is currently failing on
mainpushes too, for the same reason).未重建
cloud-functions/[[default]].js:本工作区的../OpenList-Frontend在未合并分支上(领先官方 main 两个提交),在此重建会把未审查的前端产物打进
EdgeOne 包;
edgeone-artifact-guard工作流会在 main 上自动刷新(该检查目前在main 的 push 上同样是失败的,原因相同)。
envexplicitly through the ~24 no-arggetDb()call sites in
internal/op/storage.ts(theTODOindb.ts) would remove the"last request wins" cache-key ambiguity entirely. Still tracked separately.
把请求级
env显式透传到internal/op/storage.ts的约 24 处无参getDb()调用(
db.ts中的TODO)可以从根上消除"最近一次请求作为缓存键"的歧义,仍作为独立事项跟进。
KV_BINDING_NONE_TTL_MS = 1000and the once-per-process warning aredeliberate trade-offs: they keep the existing "retry once the Blob SDK becomes
ready" behaviour while removing per-request duplicates. If a deployment ever
gained a binding mid-process, the warning would not repeat — acceptable for
an environment-configuration conclusion.
KV_BINDING_NONE_TTL_MS = 1000与"每进程一次"的告警是有意的权衡:既保留"Blob SDK 稍后就绪可重试"的既有行为,又消除每请求重复。若某部署在进程存活期间
才获得绑定,该告警不会再次出现——对"环境配置结论"而言可以接受。