perf(authority): batch verified File receipt reads - #5404
Conversation
huangruiteng
left a comment
There was a problem hiding this comment.
Request changes conclusion (author-owned PR; GitHub blocks formal self-review)
评审 head:680eca49cb3e374870dea3113efa20b08d367abf。不可变对照为实际 merge base/提交父节点 db3672f3c6646a75878158d66c79a1a5a32ceb32;覆盖本 PR 全部 4 个文件。
[P2] 逐项校验跳过稀疏数组,成功结果中出现未定义回执项。 file_authority_store.ts:483 使用 map 验证 ID,两个 map 都跳过数组空洞。new Array<string>(1) 无需类型断言即可触发:原实现通过公共 helper 返回一个明确的 failed/invalid_operation_id 项;本 head 返回 status: "receipts",但 results[0] 为 undefined,JSON 序列化为 [null]。因此结果违反 AuthorityStoreReceiptResult[] 及“每个请求位置都有回执结果”的公共合同,读取 results[i].status 的消费者可能抛异常。SQLite 在相同输入下拒绝整批。
const ids = new Array<string>(1);
const result = await readAuthorityReceipts(store, ids);
// 本 head:{status: "receipts", results: [<empty item>]}最小修复是在第一次 await 前用会访问每个位置的密集拷贝验证,例如 Array.from(operationIds, id => requireAuthorityStoreId(id, "operation id")),保留目前的输入快照。直接 provider 和公共 helper 都补一个全空洞/混合空洞负例,要求整批返回明确失败。独立结果形状断言在对照通过、在本 head 失败。该问题限于进程内稀疏数组;当前 archive 调用构造密集 ID 数组,未观察到它被此问题破坏,也没有证据说明已写入回执被伪造。
动机
Archive restore/audit 已经使用通用的 1–64 项批量回执合同;File 仍走逐条 fallback,每条都重新读取并哈希同一个 envelope。已有缓存只复用已验证索引,不能消除这些重复字节读取。把批量路径放在现有 File provider 能减少真实恢复消费者的重复成本;它属于 shared authority checkpoint 的有界增量。
不做改动可以避免新增边界,但保留重复读取;跳过完整历史证明或拿 scan 代替 receipt proof 会削弱合同。当前设计复用已有批量入口和真实消费者,比增加另一套恢复机制更合适。
改动思路
公共 helper 优先调用 provider 的原生 batch。File 在 IO 前复制并验证 ID,一次 readVerified 证明完整字节、store identity 和保留历史,再从已验证索引按调用顺序查询,每个 found 项各自 structuredClone。单条查询保留原有非法 ID 错误,再委托 batch,减少重复结果映射。
决策和证明仍在 TypeScript 的既有 authority provider;持久格式、writer、lease 和执行权限均未新增。Archive 页面继续分别校验 scan 和原始 receipt;完整 audit 最后核对 head,所以一次 batch 证明没有被误当成整次 audit 的全局快照。
具体改动
关键代码讲解
FileAuthorityStore.readReceipts(478 行):长度限制 → ID 快照 → 完整证明 → 有序结果/独立回执内容;异常由readFailure投影。正常路径正确,483 行的稀疏数组遗漏是本次阻塞点。FileAuthorityStore.readReceipt(460 行):先保留invalid_operation_id诊断,再用单项 batch;正常 found/missing、provider revision 和原始回执不变。readAuthorityReceipts(authority_store.ts,192 行,未改):原生 batch 与逐项 fallback 的共同入口,保留数量上限及结果次序;此调用路径实际复现了上述回归。checkArchivePage(authority_archive_audit.ts,27 行,未改):先比对提交页,再逐项比对原始回执;完整 audit 的最终 head 检查继续负责并发追加语义。
新增 3 个测试覆盖顺序、重复、内容隔离、IO 时调用者修改数组、长度与非法 ID、历史回执篡改、身份替换及不存在 store 的只读行为;缺少稀疏数组负例。两份中英文 ledger 同步更新 #5395 的已合入状态与本机采用边界,并明确 warm component 测量没有闭合完整恢复成本、soak 或 D2/默认资格。已核对 #5395 的 merge commit;本评审不把本机证据外推为所有 Host 已采用。
对主干的风险
在同一份隔离的 48 笔、802,997 字节 File 历史上,实际 checkArchivePage 校验 16 项的证明读取从 17 次降为 2 次。正常 found/missing、顺序、重复、完整回执、标量诊断及读前读后 head 与对照逐字段一致,SQLite 同负载对照也保持原语义。9 次暖读样本的 File batch 中位数约 10.07 → 0.60 ms,SQLite 约 5.14 → 5.44 ms;这是本地有噪声的组件测量,未独立复现文档的 1,287 笔样本,不能推导完整恢复或持续运行收益。
通过真实 File archive audit 在 scan 与 batch 之间追加一笔:对照和 head 的 exact 都返回 archive_target_changed,retained_prefix 都匹配原 48 笔。恢复写入依然由原 writer 负责;整文件重写成本未关闭。该 PR 没有改变 PostgreSQL adapter 或共享事务/写入合同,实际后端验证范围为 File 和 SQLite。
本地验证:9 个 TS authority/archive 套件 700 项通过;Python archive/retry 的实际 CLI 流程 15 项通过;控制面 typecheck、语义 advisory、DCO、diff hygiene 和 premerge 19 项 canary 均通过。不存在必需跳过或手工豁免。新增的稀疏数组独立 oracle 仍失败,并已用同一脚本在不可变对照归因到本 PR;这些通过项没有覆盖该遗漏。
语义与 CI 对齐
复用现有 receipt batch/found/missing/failed 词汇,无新状态分类、substring 规则或 domain 专属控制义务。合法输入保留合同;非法密集输入现在拒绝整批,与 SQLite 对齐,并已由测试和双语文档披露。稀疏输入的成功空洞没有披露或合法依据,需要修复。完整 diff 没有 opt-in/default-off 声明,batch 优先选择是 File 的直接行为改动。当前评审策略 wait_for_ci=false:使用上述源码本地验证,未读取或等待远端 CI。
我的整体评价
REQUEST_CHANGES,仅上述输入/结果边界缺陷阻塞。性能增量、放置边界和代码规模合理;无需为了这个修复增加框架、迁移 writer 或关闭整个 authority roadmap。面向后续维护的收敛已体现在单条与批量共用结果映射;下一步是在同一 owner 补密集验证及负例,再复核完整批量合同。UI、Lark 与 CLI 参数无需伴随变化,因为有效输入的公共 payload 未变,实际 archive CLI 已验证;该结论不表示已安装 Host 或父级持续运行验收完成。修复后的 head 需要重新评审,合并由维护者处理。
English verdict: REQUEST_CHANGES - 680eca4: useful verified File receipt batching, but sparse arrays bypass ID validation and produce undefined successful result items; fix dense validation and add a regression. 700 TS tests, 15 Python/CLI tests, typecheck and 19 canaries passed; the independent sparse-array oracle fails only at this head.
Signed-off-by: huangruiteng <huangrt01@163.com>
Signed-off-by: huangruiteng <huangrt01@163.com>
680eca4 to
c7c1534
Compare
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
自审结论:APPROVE,无未解决的阻塞项。审查对象为 c7c1534bcb38ff5ffe0c9e10c12fb115c3ec21f5,基线为 85dc7cec83f923c1ef5d6148957f5836ed400251。本次重新审查完整四文件差异,另核对上次稀疏数组问题的修复,没有沿用旧 head 的通过结论。
动机
Archive restore 和 audit 已按页核对原始回执,但 File 没实现已有的批量接口,公共 helper 会逐条调用单条查询,反复读取和验证整个历史文件。累积历史越多,这项重复成本越明显。此次在现有 provider 内消除重复验证,保持历史恢复的真实性;它是恢复读成本的有效增量,不是 SQLite 默认准入的完成声明。
改动思路
复用 AuthorityStore.readReceipts 和既有 readVerified:一批请求只建立一个经过完整字节、store identity 和历史校验的视图,再从该视图的回执索引按调用顺序取值。没有新增协议版本、长期缓存、手工同步的状态或 Python 决策源。更小的方案如只扫描查询结果、加超时或裁剪回执,不能同时解决重复成本和真实性要求;直接把批量接口改成所有 provider 的强制接口则会引入无必要的兼容成本。
具体改动
File 新增有界批量方法,单条查询复用一项批量的映射,并保留原有 invalid_operation_id 诊断。合法请求保留顺序、重复 ID、缺失项、原始 cursor/revision 和嵌套 metadata;每个 File 返回内容独立,修改一份重复结果不会污染另一份或持久历史。两个 RFC 交付记录同步说明已合入的 Python facade 退役,并明确此次优化仍未闭合整次恢复成本。
上次阻塞的根因是 Array.map 跳过数组空位:string[] 类型并不保证运行时数组稠密,成功结果可能含 undefined 项。现在按长度逐下标验证并在任何异步存储访问前复制合法 ID;特意不用数组自己的迭代器,防止自定义 iterator 掩盖空位。新回归覆盖全空位、混合删除空位、自定义 iterator,以及可读和不可读存储,通过 provider 和公共 helper 两种入口确认先返回输入错误。有效请求期间调用方修改数组、旧历史回执被篡改、identity 被替换等路径也有验证。
关键代码讲解
FileAuthorityStore.readReceipts(file_authority_store.ts:478):先检查数量和每个位置的 ID,再调用readVerified,最后按序查找并复制回执。证明入口仍是完整历史验证,扫描结果没有替代回执证明。FileAuthorityStore.readReceipt(同文件第 460 行):保留单条非法 ID 的原有错误优先级,其余查询委派给批量方法,从而不再维护两份结果映射。checkArchivePage(authority_archive_audit.ts:27):未修改的实际消费者,先核对交易页,再经公共 helper 验证每笔原始回执及其版本。它证明这次加速已接入真实恢复路径。
对主干的风险
输入拒绝粒度有一个明确变化:File 的 native batch 遇到非法 ID 会拒绝整批,旧 fallback 则返回逐项结果;单条 API 的诊断保持不变。正常业务调用传入稠密合法 ID,结果逐字段一致,没有新增用户操作、UI/Lark 配置或状态迁移。读缺失与非法请求不写入存储,篡改历史仍失败,因此没有以加速换取弱验证。
最终隔离回归 713 项、真实 File/SQLite CLI 回归 15 项、完整 control-plane typecheck 和全部 19 项风险 premerge 检查通过。初次并发回归曾有一项跨进程消费者超时;同一项在不可变基线与当前 head 单独重跑均通过,最终全套隔离顺序运行也通过,没有提高超时或修改期待值。公共入口形状 oracle 在基线和修复 head 通过,恢复原先 .map 的语义变体会使独立断言失败。CI 未查询,遵循当前 review capability 的本地验证政策。
在相同的隔离 1,287 笔历史上,每组九次暖读,File 16 条批量查询中位数为 361.59→25.77 ms;实际 archive page proof 的单次测量为 797.24→478.40 ms,原始回执、head 和证明结果一致。未修改 SQLite 对照批量中位数为 112.49→117.23 ms。此测量不能外推为冷读、完整恢复或跨平台默认资格;File 每笔恢复仍可能重写完整保留文件,相关工作继续保留。
我的整体评价
已有真实调用方和相同负载下的收益,生产机制集中在现有 TS File owner,额外测试针对真实可复发的恢复合同。上次阻塞已修复并通过缺陷敏感性验证;未来修改批量读取只需维护同一结果映射和原有验证入口。公开材料未包含私有快照、原始日志、凭证或本机路径。可以按用户对本 PR 的明确自合并授权合并,父级 D2/default 准入与整历史恢复验收仍保持未完成。
English verdict: APPROVE
File archive restore/audit already checks original receipts in bounded pages, but the File provider previously reread and verified the complete durable envelope once per operation. Implement the existing optional
AuthorityStore.readReceiptscontract in File so each batch uses one fully verified view and resolves every original receipt in its index.The batch preserves caller order, duplicate/missing IDs, original nested receipt bodies and corrupt-history refusal. Scalar lookup reuses the same mapping and retains its
invalid_operation_iddiagnostic. Invalid native batches intentionally fail as a whole. The review fix validates every array position before IO: sparse arrays and custom iterators cannot produce successful undefined items. No schema, writer, CLI response, provider default or authority grant changes. The bilingual retirement checkpoint reconciles merged #5395 and keeps full File restore-write cost and SQLite D2 acceptance open.Validation on
c7c1534bcb38ff5ffe0c9e10c12fb115c3ec21f5, rebased onto85dc7cec83f923c1ef5d6148957f5836ed400251:.mapdefect is restored. Durable tests also cover invalid-before-IO precedence, caller mutation during IO, detached duplicates, forged historical receipts, identity mismatch and read-only missing stores.This extends the existing TS File storage owner without a new capability or Python decision owner. Frontend/Lark require no companion change because receipt projections and user interactions are unchanged, covered by scalar/batch and real CLI parity. Private snapshots, raw measurements and local probes are excluded. The owner explicitly authorized self-review and self-merge for this PR; the exact-head review and immediate merge-readiness readback precede merging.