fix(v0.8.0): 全量回归审查 —— 1 处 P0 数据丢失 + 4 处 P1 + 9 处 P2 根因修复
方法:四个对抗性子代理分头审查(数据正确性 / 文档宣称 vs 实现 / 公共 API 契约 / 测试质量),每条结论要求可复现证据;逐条复核 + 探针确认 + 变异验证(40 项全部 被对应用例拦住)。 P0:事务活跃期间 repair()/close()/周期 checkpoint 推进 WAL 水位 → 已 COMMIT 的 事务整批消失且恢复报告"干净"。根因 hasPendingFlushData()/computeDurableLsn() 不看 txnSnapshot;守卫此前只在 CheckpointManager 两个回调里。修复:守卫下沉到 computeDurableLsn() 与 advanceWalCheckpoint() 入口(唯一实现)。 P1: - WAL 前缀缺失丢弃整段活分片(回退上一代 manifest 时 kept 为空)→ 前缀缺失单独 记录,后缀照常重放;仅 fromLsn === 0 时才算真异常 - 孤儿回收门槛只看引擎层 dataLossSuspected,漏掉 LSM 层被丢的 SSTable → 统一 describeRecoveryDamage() 聚合判定(损坏时绝不删"引用不到"的文件) - vacuum() 逐层压缩绕过维护链 → vacuumLevels() 每层作为维护链任务执行 - reclaimRetiredNow() 无视在途读者(读者把"已退休"读成"文件损坏")→ 有读者时 退化为延迟回收 P2:WAL 记录级 CRC 损坏不计数不上报;旧格式表结构记录形状损坏静默当空库; bloomFilterBitsPerKey 配置被接受却完全不生效(构建器写死默认值,实现缺陷); 幽灵 meta;介质读故障等于文件损坏的语义无用例;manifest 回读校验两条守卫无用例; 文件名≠载荷世代判定无用例;pageIdWatermark 单调性无用例;分片号两条真实不变量 无用例。 覆盖率口径(第二处漏洞):interface.ts 混着三个运行时函数(cloneRow 等)却被 描述为"纯类型、不纳入统计" → 实现搬到 src/engine/row_clone.ts;搬完门禁真的 失败(functions 93.84% < 94%),补测退化路径后通过。 测试质量:3 条空壳用例改值级断言;1 条"全损坏"用例实际只走缓存 → 拆成两条真 用例;5 秒墙钟 race 改门控 + 失败上限;setTimeout 改 whenIdle();<= 收紧为 <。 变异脚本加固:正控(干净基线必须全绿)、编译失败/0 用例单独归类、300s 超时、 逐字节 sha256 恢复校验、O_EXCL 进程锁、锚点唯一性;变异 22 → 40 项。 文档两轮订正(16 + 11 条不成立宣称):MVCC 快照隔离、backup 一致性快照、 "空洞检测截断"、体积(251,109 B / gzip 63,145 B)、测试与覆盖率数字、 "5 种存储引擎"、Tree-shakable、错误码表补 16 个码、恢复报告字段、已知限制 (回退单向 / 多实例依赖 Web Locks / manifest 体积 / 尾部 WAL 分片不可识别)。 验证:常规套件 92 套件 / 1980 用例全绿;覆盖率 90.59 / 82.59 / 94.14 / 93.50 (阈值 90/82/94/93);e2e 14/14(真实 Chromium + OPFS + CDP 崩溃); 重型套件 4 套件 / 27 用例;变异 40/40;lint + 两份 tsc 干净;dist 已重建。
This commit is contained in:
+122
-12
@@ -836,7 +836,7 @@ SSTable / 跨库搬迁),必须在 `save()` 之前补校验**。
|
||||
- PC-2 真崩溃注入(CDP `Page.crash`)+ OPFS `createWritable` 的两个写入窗口;
|
||||
- `FaultyBackend` 字节级故障注入(撕裂/bit-flip/掉电/写失败);
|
||||
- **变异验证**成为回归套件的标准做法:把修复回退到修复前的行为,对应用例
|
||||
必须失败。本轮 A15/A13/A38/A39 与 **B-6 全量 17 项**均通过该检查(可执行脚本
|
||||
必须失败。本轮 A15/A13/A38/A39 与 **B-6 全量 + 审查轮共 40 项**均通过该检查(可执行脚本
|
||||
`scripts/mutation-b6.py`,逐项打印"变异是否被拦住")——
|
||||
这是"测试真能拦住回归"与"测试只是陪跑"的分界线。
|
||||
- 测试介质的两处**忠实性**缺陷已被修正(`SharedMemoryBackend` 跨实例可见性、
|
||||
@@ -850,12 +850,12 @@ SSTable / 跨库搬迁),必须在 `save()` 之前补校验**。
|
||||
|
||||
| 门禁 | 内容 | 验证方式与实测结果 | 状态 |
|
||||
|---|---|---|---|
|
||||
| **G1** | 事故级缺陷全部修复,且每项有**值级**回归测试(非行数断言) | 12 项事故级(A1~A10、A15)+ 全部 P1 已修;每项均有独立回归套件,断言值/结构而非行数:`v080-sql-three-valued`(26 条 SQL 语义矩阵 × 4 引擎)、`v080-query-layer`(8 项 × 4 引擎)、`v080-column-resolution`、`v080-foreign-key`、`v080-aria-ddl-atomicity`、`v080-unified-validation`、`v080-case-expression`(54 项)、`v080-output-ordinals`(68 项)、`v080-kvstore-commit-point`、`v080-single-pipeline`、`v080-savepoint`、`v080-atomicity`、`v080-subscribe`、`v080-correlated` | ✅ |
|
||||
| **G1** | 事故级缺陷全部修复,且每项有**值级**回归测试(非行数断言) | 11 项事故级(A1~A11,与 §2 总账一致)+ 全部 P1 已修;每项均有独立回归套件,断言值/结构而非行数:`v080-sql-three-valued`(26 条 SQL 语义矩阵 × 4 引擎)、`v080-query-layer`(8 项 × 4 引擎)、`v080-column-resolution`、`v080-foreign-key`、`v080-aria-ddl-atomicity`、`v080-unified-validation`、`v080-case-expression`(54 项)、`v080-output-ordinals`(68 项)、`v080-kvstore-commit-point`、`v080-single-pipeline`、`v080-savepoint`、`v080-atomicity`、`v080-subscribe`、`v080-correlated` | ✅ |
|
||||
| **G2** | 入口等价性:同一 SQL 经 `query`/`queryStream`/`QueryBuilder`/事务内四条入口**逐值相等** | `v080-single-pipeline`(TABLE API 与 SQL API 逐值等价,4 引擎 × 12 项 + 跨引擎 1 项,共 57 断言);`queryStream` 与 `query` 在 16 形态 × 4 引擎逐值相等(`752bdea`);事务内入口经同一 Executor(`b20d47b`) | ✅ |
|
||||
| **G3** | 跨引擎一致性:同 schema + 同操作在 memory/disk/hybrid/aria 行为一致(含错误码) | 全部新增套件均按 `describe.each(ENGINES)` 参数化到四引擎:`v080-unified-validation`(41)、`v080-sql-three-valued`(104)、`v080-query-layer`(111)、`v080-single-pipeline`(57)、`v080-column-resolution`(32)、`v080-foreign-key`(32)、`v080-output-ordinals`(64)、`v080-case-expression` 等;错误码断言用 `rejects.toMatchObject({ code })` | ✅ |
|
||||
| **G4** | 故障注入下崩溃不变量恒成立:已确认写入零丢失 + 索引与主表一致 + 唯一约束仍生效 | 单元层:`FaultyBackend`(撕裂写 / bit-flip / 掉电丢弃 / 写失败)+ `TransactionalFileStore`(pending/commit/crash 语义)+ `CrashableStoreBackend`;`v080-kvstore-commit-point`(19 项,含三处 P0 的变异验证);`v080-atomicity`;`v080-savepoint`。e2e 层:CDP `Page.crash` 真崩溃 + OPFS `createWritable` 的 write/commit 两个窗口(`tests/e2e/opfs.spec.ts`,14 项) | ✅ |
|
||||
| **G5** | 覆盖率真实且不倒退:`collectCoverageFrom` 无实现文件被排除;CI 有 `coverageThreshold`;README 数字与产出一致 | `jest.config.cjs` 仅排除两个**纯类型**文件(`engine/interface.ts`、`query/ast.ts`);阈值 statements 90 / branches 82 / functions 94 / lines 93;CI 常规 job 带 `--coverage`;README 写明产出命令与四个实测数字(90.43 / 82.21 / 94.27 / 93.44),且本轮经历"阈值真的失败 → 补测/删死代码"的闭环(见 `d14663e`) | ✅ |
|
||||
| **G6** | 文档与实现一致:README/CHANGELOG/site 中每一条能力宣称都有对应测试或已删除 | §8 的 **12 条**全部收口:订阅(已接线)、LZ4 页面压缩(A38 已修)、`./migration` 导出、react/vue 产物+d.ts+peerDeps、`disconnect` 进类型、priority(改为实现符合文档)、MVCC 快照隔离(改为如实描述)、backup 一致性快照(改为如实描述并列入已知限制)、"5 种引擎"(改为 4 模式 + 3 后端)、`diskEngine` 生效范围(改为仅 aria)、"在线一致性快照"(改为全库导出)、钩子契约(CONTRIBUTING 写明返回值语义) | ✅ |
|
||||
| **G4** | 故障注入下崩溃不变量恒成立(**限定于矩阵覆盖的窗口**)+ 索引与主表一致 + 唯一约束仍生效 | 单元层:`FaultyBackend`(撕裂写 / bit-flip / 掉电丢弃 / 写失败)+ `TransactionalFileStore`(pending/commit/crash 语义)+ `CrashableStoreBackend`;`v080-kvstore-commit-point`(19 项,含三处 P0 的变异验证);`v080-atomicity`;`v080-savepoint`。e2e 层:CDP `Page.crash` 真崩溃 + OPFS `createWritable` 的 write/commit 两个窗口(`tests/e2e/opfs.spec.ts`,14 项)。<br>**⚠️ 限定说明(v0.8.0 审查修正)**:矩阵覆盖的是"介质/进程在写入窗口内崩溃"这一类故障;它**不能**证明"任何情况下零丢失"。全量回归审查在矩阵覆盖范围之外发现 1 处**已确认写入丢失**(事务活跃期间 `repair()`/checkpoint 推进 WAL 水位 → 已 COMMIT 的事务整批消失,见附录 I),已修复并补上回归测试与变异验证。因此本门禁的结论按"矩阵范围内成立"表述,不宣称普遍零丢失 | ✅(有限定) |
|
||||
| **G5** | 覆盖率真实且不倒退:`collectCoverageFrom` 无实现文件被排除;CI 有 `coverageThreshold`;README 数字与产出一致 | `jest.config.cjs` 仅排除两个**纯类型**文件(`engine/interface.ts`、`query/ast.ts`);阈值 statements 90 / branches 82 / functions 94 / lines 93;CI 常规 job 带 `--coverage`;README 写明产出命令与四个实测数字(最终值见 README「项目指标」表),且本轮经历"阈值真的失败 → 补测/删死代码"的闭环(见 `d14663e`) | ✅ |
|
||||
| **G6** | 文档与实现一致:README/CHANGELOG/site 中每一条能力宣称都有对应测试或已删除 | **第一轮**:§8 的 **12 条**收口:订阅(已接线)、LZ4 页面压缩(A38 已修)、`./migration` 导出、react/vue 产物+d.ts+peerDeps、`disconnect` 进类型、priority(改为实现符合文档)、MVCC 快照隔离(改为如实描述)、backup 一致性快照(改为如实描述并列入已知限制)、"5 种引擎"(改为 4 模式 + 3 后端)、`diskEngine` 生效范围(改为仅 aria)、"在线一致性快照"(改为全库导出)、钩子契约(CONTRIBUTING 写明返回值语义)。<br>**第二轮(v0.8.0 全量回归审查)**:文档宣称 vs 实现逐条核对又发现 **16 条不成立**(MVCC 快照隔离、backup 一致性快照、`bloomFilterBitsPerKey` 配置项无效、gzip 体积、测试/覆盖率/套件数陈旧、错误码表缺 16 个码、WAL 空洞语义、mutation 数量、"分片号只增不减"等),全部在附录 I 中逐条订正;其中 `bloomFilterBitsPerKey` 是**实现缺陷**(配置被接受却不生效),已修实现而非改文档 | ✅(两轮) |
|
||||
|
||||
**额外要求**:CI lint 已去掉 `continue-on-error`(真正阻断);`tsconfig.test.json` 对 `tests/` 做类型检查并修复了 **103 个**被 babel 剥离类型掩盖的类型错误。 ✅
|
||||
|
||||
@@ -876,13 +876,14 @@ SSTable / 跨库搬迁),必须在 `save()` 之前补校验**。
|
||||
| `src/engine/aria/store/manifest.ts`(新增) | `__aria_manifest_<generation>`:magic + formatVersion + generation + 头部 CRC + 载荷 CRC;`ManifestStore.load/claimOwnership/commit`;先写后验;保留两代;损坏世代**显式失败** |
|
||||
| `src/engine/aria/index/lsm.ts` | 冻结表一等状态(id/LSN/重试/提交窗口);读路径 epoch + 结构版本重试;compaction 不再摘整层、底部层原地合并回收墓碑、按层 `compacting` 集合;flush/maintenance 双链;恢复报告 |
|
||||
| `src/engine/aria/index.ts` | manifest 装载 → 旧格式迁移 → 认领所有权;`commitManifest()`(唯一提交入口);`advanceWalCheckpoint()`(先算边界 → 提交 → 再删除);表结构随 manifest 提交;恢复报告 `getRecoveryReport()`;删除引擎层全部 `prefetch*` 依赖 |
|
||||
| `src/engine/aria/wal/log.ts` / `segmented_store.ts` | LSN 全库单调(manifest 记高水位);`planKeepFrom` / `truncateBefore`;**分片号只增不减**;空洞(含前缀缺失)如实上报;按水位清理旧格式键 |
|
||||
| `src/engine/aria/wal/log.ts` / `segmented_store.ts` | LSN 全库单调(manifest 记高水位);`planKeepFrom` / `truncateBefore`;**分片号绝不回退、也绝不低于 manifest 水位**(整体清空后允许复用最后用过的号 —— 见附录 I 的语义澄清);内部空洞与记录级 CRC 损坏如实上报(`gaps` / `corruptRecords`),水位从未推进时的前缀缺失同样按空洞上报;按水位清理旧格式键 |
|
||||
| `src/engine/aria/store/page_sstable_store.ts` | 活跃 + 退休两张页面映射(在途读者按 id 读取不再被 compaction 摘除影响);`registerPageIds` |
|
||||
| `src/engine/aria/store/file_manager.ts` | 页面水位取 manifest / 旧 meta / 现存最大 id 三者最大(单调、永不复用);`__aria_meta` 降级为兼容提示 |
|
||||
| `src/engine/aria/index/sstable.ts` | 三份解析循环合并为 `iterEntries()` 一份,越界策略统一 |
|
||||
| `src/engine/aria/index/merge_iterator.ts` | 胜出来源的补充推迟到下一次 `next()`(提前终止不多算) |
|
||||
| `src/engine/aria/wal/checkpoint.ts` | checkpoint 只落 memtable(`flushMemtables?`),不等 compaction |
|
||||
| `tests/v080-b6-single-commit-point.test.ts`(新增 63 项) | 上述每一条的回归 + manifest 严格校验表驱动 25 例;`scripts/mutation-b6.py`(22 项变异验证,全部被拦住) |
|
||||
| `tests/v080-b6-single-commit-point.test.ts` | 上述每一条的回归 + manifest 严格校验表驱动 **24** 例;v0.8.0 审查后扩到 **96 项**(新增事务水位 P0、前缀空洞语义、在途读者与退休文件、孤儿回收门槛、稀疏/损坏分支、形状校验等)|
|
||||
| `scripts/mutation-b6.py` | **40 项**变异验证(B-6 的 22 项 + 审查轮的 18 项),全部被对应用例拦住;脚本自带正控、编译失败区分、超时、逐字节恢复校验与进程锁(见 CONTRIBUTING)|
|
||||
|
||||
### H.2 提交顺序(唯一不变量)
|
||||
|
||||
@@ -920,10 +921,10 @@ __aria_manifest_<gen> 提交(单文件原子写 + 回读校验)
|
||||
### H.4 验收(可复现)
|
||||
|
||||
```bash
|
||||
# 1) B-6 回归(63 项)
|
||||
# 1) B-6 回归(96 项)
|
||||
npx jest tests/v080-b6-single-commit-point.test.ts
|
||||
|
||||
# 2) 变异验证:把每个修复回退到修复前行为,对应用例必须失败(22 项)
|
||||
# 2) 变异验证:把每个修复回退到修复前行为,对应用例必须失败(40 项)
|
||||
python3 scripts/mutation-b6.py
|
||||
|
||||
# 3) 随机压力 / 检查点崩溃恢复(发现缺陷 1 的套件)
|
||||
@@ -933,13 +934,122 @@ npx jest tests/engine/aria-repair-hardening.test.ts tests/engine/aria-kv-backend
|
||||
npx jest --testPathIgnorePatterns='/node_modules/|/tests/e2e/|aria-prod-load|kvstore-stress|aria-matrix-audit|aria-idx-flush-race'
|
||||
```
|
||||
|
||||
**实测数字**:常规套件 **1935 通过 / 91 套件**;覆盖率 语句 90.34% / 分支 82.16% /
|
||||
函数 94.06% / 行 93.23%(阈值 90/82/94/93,全部通过)。
|
||||
**实测数字**(审查轮收尾复测,详见附录 I):常规套件 **1980 通过 / 92 套件**;
|
||||
覆盖率 语句 90.59% / 分支 82.59% / 函数 94.14% / 行 93.50%(阈值 90/82/94/93,全部通过);
|
||||
e2e 14/14;变异验证 **40/40** 被拦住。
|
||||
|
||||
**恢复报告**:`engine.getRecoveryReport()` 返回
|
||||
`{droppedSSTables, dataLossSuspected, walGaps, legacyImported, manifestFallback}` ——
|
||||
`{droppedSSTables, dataLossSuspected, walGaps, droppedWALRecords, legacyImported, manifestFallback}` ——
|
||||
"损坏被自愈了多少、有没有真正丢数据"从此是**可读的返回值**,而不是只能翻日志。
|
||||
|
||||
**新增错误码**:`ARIA_MANIFEST_CORRUPT`、`ARIA_MANIFEST_WRITE_FAILED`、
|
||||
`ARIA_MANIFEST_NOT_LOADED`、`ARIA_LEGACY_META_CORRUPT`、`ARIA_SSTABLE_READ_FAILED`、
|
||||
`ARIA_SSTABLE_SAVE_CONTRACT`、`ARIA_WAL_GAP`、`ARIA_WRITE_LOST`、`STALE_INSTANCE`。
|
||||
|
||||
---
|
||||
|
||||
## 附录 I · v0.8.0 全量回归审查(发版前最后一轮)
|
||||
|
||||
> 触发:用户要求"做完整全量的回归 review"。方法:**四个对抗性子代理**分头审查
|
||||
> (① 数据正确性与崩溃语义 ② 文档宣称 vs 实现 ③ 公共 API 契约 ④ 测试质量与
|
||||
> 覆盖盲区),每一条结论都要求**可复现证据**;我再逐条复核、写探针确认、并在
|
||||
> `src/` 上做**变异验证**(把修复回退 → 对应用例必须失败)。
|
||||
> 详细缺陷叙述见 `CHANGELOG.md` 的 v0.8.0「全量回归审查」一节;本节只列结论与证据。
|
||||
|
||||
### I.1 确认缺陷(14 项:1×P0 / 4×P1 / 9×P2)
|
||||
|
||||
| 级别 | 缺陷 | 证据(可复现) | 变异 |
|
||||
| --- | --- | --- | --- |
|
||||
| **P0** | 事务活跃期间 `repair()` / `close()` / 周期 checkpoint 推进 WAL 水位 → 已 `COMMIT` 的事务整批消失,且恢复报告"干净" | `tests/v080-b6-single-commit-point.test.ts` ›「BEGIN → INSERT → repair() → COMMIT → 崩溃」;根因 `hasPendingFlushData()` 不看 `txnSnapshot` | R1 |
|
||||
| P1 | WAL 前缀缺失把**整段活分片**丢掉(回退上一代 manifest 时 `kept` 为空) | 同套件「fromLsn > 0 时前缀缺失只记诊断,后缀分片照常重放」 | R4 |
|
||||
| P1 | 孤儿回收门槛只看引擎层 `dataLossSuspected` → LSM 层丢过 SSTable 时仍会删"引用不到"的活页(不可逆) | 「有损坏迹象时:引用不到的东西一律保留」;统一判定 `describeRecoveryDamage()` | R3 |
|
||||
| P1 | `vacuum()` 逐层压缩绕过维护链(与后台 compaction 并发写同一目标层) | 「vacuumLevels 的逐层压缩必须挂在维护链上」;**注意**:引擎层 `vacuum()` 的 `flush()` 已会 drain 维护链,故该用例锁定的是 LSM 层不变量 | — |
|
||||
| P1 | `reclaimRetiredNow()` 无视在途读者(读者把"已退休"读成"文件损坏") | 「有在途读者时强制回收必须退化为延迟回收」 | R2 |
|
||||
| P2 | WAL 记录级 CRC 损坏只打日志(恢复报告无感) | 「一条记录 CRC 失败 → corruptRecords > 0 且标记丢数据」 | R9 |
|
||||
| P2 | 旧格式表结构记录**形状**损坏(数组 / null / 非对象条目)静默当空库 | 「旧格式表结构记录的形状校验」5 条 | R16 |
|
||||
| P2 | **`bloomFilterBitsPerKey` 配置被接受却完全不生效**(构建器写死默认位数) | 「bloomFilterBitsPerKey 必须真的生效」2 条(段大小 + 无 false negative) | R17 / R18 |
|
||||
| P2 | 丢弃损坏文件后不同步内存层数组(幽灵 meta) | 「compaction 丢弃损坏文件后 levels 不留幽灵 meta」 | R5 |
|
||||
| P2 | 介质读故障被折叠成"文件损坏"的路径无用例 | 「介质读故障 ≠ 文件损坏:compaction 必须失败且一个 meta 都不许丢」 | R11 |
|
||||
| P2 | manifest 回读校验两条守卫无用例(吞写 / 内容坏) | 「写成功但读不回来」「写下去的内容读回来是坏的」 | R8 |
|
||||
| P2 | "文件名世代 ≠ 载荷世代"的世代无效判定无用例 | 「文件名世代与载荷世代不一致 → 该世代无效」 | R6 |
|
||||
| P2 | `pageIdWatermark` 单调性无用例(水位可回退 → 页面 id 复用) | 「水位是单调下限:计数器回退也不许把 manifest 水位带回低值」 | R7 |
|
||||
| P2 | 分片号两条真实不变量无用例(清空后不回退到 0 / 新写入不低于 manifest 水位) | 「整体清空后分片号绝不回退」「写入永不落到 manifest 水位下限之下」 | R13 / R14 |
|
||||
|
||||
**语义澄清(不是缺陷,但此前文档与实现不一致)**:
|
||||
- **"分片号只增不减"** 的准确语义是"**绝不回退、绝不低于 manifest 水位**",
|
||||
整体清空(`truncate()`)后允许复用**最后用过的那个号** —— 记录自带 LSN,
|
||||
旧世代记录按水位跳过,因此复用安全;真正致命的是"回退到 0"(新记录落在
|
||||
`startSegment` 之下 → 恢复时被过滤掉)。已同步 README / H.1 / CHANGELOG。
|
||||
- **尾部 WAL 分片丢失**无法从介质自身识别(没有"它本该存在"的证据),
|
||||
`ARIA_WRITE_LOST` 只兜住"有未落盘冻结表却重放不到任何记录"的情况 —— 已写入已知限制。
|
||||
- **`vacuumLevels()` 里 `drainMaintenance()` 那一行未被变异验证**:它在实现上只是
|
||||
优化(抢在链前重算层内文件数),去掉后仍然串行,只是会多排几个空任务。
|
||||
|
||||
### I.2 测试质量修正("绿"≠"有保护")
|
||||
|
||||
- 3 条空壳用例(孤儿回收门槛 / 强制回收 / 页面映射退休)改为**值级断言**;
|
||||
- 1 条"整层文件全损坏"用例实际**走的是缓存**(从未读介质)→ 拆成两条真用例
|
||||
(介质读故障 / 文件真残缺);
|
||||
- 5 秒墙钟 `Promise.race` → 门控信号 + **失败上限**(超时只让测试失败,
|
||||
不会让它在实现错误时"碰巧通过");`setTimeout` 睡眠 → `whenIdle()` 确定性等待;
|
||||
- `<=` 收紧为严格 `<`(冻结意图水位必须**严格低于**意图起点);
|
||||
- 删除死代码(`const inner` / `void manifest`)与误名的"非 JSON"用例。
|
||||
|
||||
### I.2b 覆盖率口径的第二处漏洞(已根因修复)
|
||||
|
||||
审查发现 README / `jest.config.cjs` / 本附录 G5 都声称"只排除两个纯类型文件、
|
||||
可执行语句为 0",但 `engine/interface.ts` 内含三个运行时函数
|
||||
(`cloneRow` / `cloneRowFallback` / `cloneRows`)并被 Memory 引擎与 AriaEngine 调用
|
||||
—— 即真实实现代码一直在覆盖率统计之外(与 G5 声称根治的问题同类)。
|
||||
处理:实现搬到 `src/engine/row_clone.ts`,`interface.ts` 变回纯类型;
|
||||
搬完后门禁**真的失败了**(functions 93.84% < 94%),被迫补测退化路径
|
||||
(`tests/engine/row-clone.test.ts`,8 项),随后全绿。这是"口径修正 → 门禁真的咬人"的闭环。
|
||||
|
||||
### I.3 文档订正(第二轮 G6:16 条不成立的宣称)
|
||||
|
||||
MVCC 快照隔离(docs.html ×3 + demo.html + `mvcc.ts` 文件头)、`backup()`
|
||||
"一致性快照"(`interface.ts` 注释)、"空洞检测截断"、"~105KB / gzip ~27KB"
|
||||
(实测 251,109 字节 / gzip 63,145 字节)、测试与覆盖率数字(README badge/正文、site 徽标与数字卡、
|
||||
CHANGELOG)、"5 种存储引擎"(→ 4 模式 + 3 后端)、"Tree-shakable"(→ 多格式输出)、
|
||||
错误码表缺 **16 个**码、CONTRIBUTING 变异数量(17 → 40)、PLAN 附录 G 的
|
||||
"12 项事故级"(→ 11 项,A1~A11)、G4"零丢失"(限定为矩阵覆盖范围内)、
|
||||
H.1"25 例"(→ 24 例)、"分片号只增不减"(→ 准确语义)。
|
||||
|
||||
**第二轮核对(独立子代理,只读审计)**:修正后再做一次"宣称 vs 实现"逐条核对,
|
||||
又发现 11 条不一致(CONTRIBUTING 的旧体积/旧测试数、README 的"空洞检测截断"残留、
|
||||
`getRecoveryReport()` 字段枚举漏 `droppedWALRecords`、PLAN 的 94-vs-96、
|
||||
CHANGELOG 历史段落的"快照隔离"表述、docs.html 缺记录级 CRC 上报、
|
||||
以及上面 I.2b 的覆盖率口径问题)——**全部已订正**,其中覆盖率口径是改实现而非改文档。
|
||||
|
||||
### I.4 验证(v0.8.0 收尾复测,全部可复现)
|
||||
|
||||
```bash
|
||||
# 1) B-6 + 分片 WAL 套件(96 + 15 项)
|
||||
npx jest tests/v080-b6-single-commit-point.test.ts tests/engine/aria-wal-segment.test.ts
|
||||
|
||||
# 2) 全量常规套件(不含重型与 e2e)
|
||||
npx jest --testPathIgnorePatterns='/node_modules/|/tests/e2e/|aria-prod-load|kvstore-stress|aria-matrix-audit|aria-idx-flush-race'
|
||||
# → 92 套件 / 1980 用例全绿
|
||||
|
||||
# 3) 覆盖率(与 CI 常规 job 完全相同的命令)
|
||||
npx jest --coverage --coverageReporters=text-summary --testPathIgnorePatterns='/node_modules/|/tests/e2e/|aria-prod-load|kvstore-stress|aria-matrix-audit|aria-idx-flush-race'
|
||||
# → Statements 90.59% / Branches 82.59% / Functions 94.14% / Lines 93.50%(阈值 90/82/94/93)
|
||||
|
||||
# 4) e2e(真实 Chromium + 真实 OPFS + CDP Page.crash)
|
||||
npx playwright test # → 14/14
|
||||
|
||||
# 5) 变异验证(40 项;脚本自带正控、编译失败区分、超时、逐字节恢复校验、进程锁)
|
||||
python3 scripts/mutation-b6.py # → 40/40 全部被拦住
|
||||
|
||||
# 6) 静态门禁
|
||||
npm run lint && npx tsc -p tsconfig.json --noEmit && npx tsc -p tsconfig.test.json --noEmit
|
||||
```
|
||||
|
||||
### I.5 未修复 / 保留项(诚实清单)
|
||||
|
||||
| 项 | 现状 | 为什么保留 |
|
||||
| --- | --- | --- |
|
||||
| e2e 未覆盖"manifest 写入窗口"崩溃 | 现有 e2e 覆盖 OPFS 的 `write()`/`commit()` 两个窗口(数据文件),未按**文件名**门控 manifest 的写入窗口 | 需要 harness 支持按 key 名门控 OPFS 写(`getFileHandle` 名追踪 + `armCrashOnOpfsWrite({file})`);收益有限(manifest 的先写后验 + 两代保留已在单测层用字节级损坏覆盖) |
|
||||
| `clearAll()` 重置 `manifest.namespaces` 时 `nextSstableId` 归零 | 页面水位保留(媒体可能仍有页面),SSTable 编号在介质已清空的前提下重新开始 | 介质被整体抹掉,编号复用无冲突对象;真要严格只需把 `nextSstableId` 也纳入单调水位 —— 当前无收益 |
|
||||
| 无 Web Locks 环境的多实例并发写 | 降级为提交点冲突检测(`STALE_INSTANCE`),**不支持**并发写 | 客户端没有跨标签页互斥原语时无法保证;已写入已知限制 |
|
||||
| manifest 体积随 SSTable 数增长 | 单文件整体重写 + 保留两代,无增量/分层 | 属设计取舍(单一提交点的代价),已写入已知限制;收敛手段是 `vacuum()` 合并层级 |
|
||||
|
||||
Reference in New Issue
Block a user