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:
thzxx
2026-09-15 16:33:40 +08:00
parent c3757f486c
commit 0b44620721
34 changed files with 4026 additions and 641 deletions
+10 -10
View File
@@ -153,9 +153,9 @@
<!-- Hero -->
<section class="hero">
<div class="container">
<div class="badge" style="margin-bottom:24px;"><span class="dot"></span> v0.8.0 根治性迭代 — 1935 测试 91 套件 · 语句/分支/函数/行覆盖率 90.34% / 82.16% / 94.06% / 93.23% · UPDATE/DELETE 子查询正确执行 · 主键非空强制 · DROP INDEX 保留 UNIQUE 约束 · findStream 真惰性 · 参数化查询 · 自研 KVStore 事务引擎 · AriaEngine LSM+WAL+MVCC · 崩溃恢复</div>
<div class="badge" style="margin-bottom:24px;"><span class="dot"></span> v0.8.0 根治性迭代 — 1980 测试 92 套件 · 语句/分支/函数/行覆盖率 90.59% / 82.59% / 94.14% / 93.50% · UPDATE/DELETE 子查询正确执行 · 主键非空强制 · DROP INDEX 保留 UNIQUE 约束 · findStream 真惰性 · 参数化查询 · 自研 KVStore 事务引擎 · AriaEngine LSM+WAL+MVCC · 崩溃恢复</div>
<h1>前端的 <span class="gradient-text">SQL 数据库</span></h1>
<p>TypeScript 原生构建,5 种存储引擎,支持完整 SQL 查询。<br>零运行时依赖,开箱即用。AriaEngine 自研引擎:LSM-Tree + WAL 同步 + MVCC。</p>
<p>TypeScript 原生构建,4 种存储模式 + 3 种存储后端,支持完整 SQL 查询。<br>零运行时依赖,开箱即用。AriaEngine 自研引擎:LSM-Tree + WAL 同步 + MVCC。</p>
<div class="actions">
<a href="demo.html" class="btn btn-primary" style="font-size:1.05rem;padding:14px 32px;">▶ 在线演示</a>
<a href="docs.html" class="btn btn-outline" style="font-size:1.05rem;padding:14px 32px;">📖 API 文档</a>
@@ -233,8 +233,8 @@ npm install @metona-team/metona-sqlark
<div class="feature-grid">
<div class="feature-card">
<div class="icon">🧠</div>
<h3>5 种存储引擎</h3>
<p>Memory / KVStore / OPFS / Hybrid / <strong>AriaEngine</strong>。Aria 是自研 LSM-Tree 页面式引擎,支持 WAL 同步崩溃恢复 MVCC 事务隔离。</p>
<h3>4 种存储模式 + 3 种后端</h3>
<p>模式:Memory / Disk / Hybrid / <strong>AriaEngine</strong>;后端:OPFS / KVStore / Memory。Aria 是自研 LSM-Tree 页面式引擎,支持 WAL 同步崩溃恢复与事务回滚(版本链仅作事务内 undo,事务串行 —— 非 MVCC 快照隔离</p>
</div>
<div class="feature-card">
<div class="icon"></div>
@@ -279,7 +279,7 @@ npm install @metona-team/metona-sqlark
<div class="feature-card">
<div class="icon">📦</div>
<h3>零运行时依赖</h3>
<p>纯 TypeScript 实现,不依赖任何第三方库。Tree-shakableUMD/ESM/CJS 多格式输出,~27KB gzip</p>
<p>纯 TypeScript 实现,不依赖任何第三方库。UMD/ESM/CJS 多格式输出,min 产物实测 251,109 字节 / gzip 63,145 字节</p>
</div>
<div class="feature-card">
<div class="icon">📊</div>
@@ -414,12 +414,12 @@ npm install @metona-team/metona-sqlark
<p>MetonaSqlark 的核心指标</p>
</div>
<div class="stats">
<div class="stat-card"><div class="num">1872</div><div class="label">测试用例(+14 e2e</div></div>
<div class="stat-card"><div class="num">93.44%</div><div class="label">行覆盖率</div></div>
<div class="stat-card"><div class="num">~27KB</div><div class="label">gzip 体积</div></div>
<div class="stat-card"><div class="num">5</div><div class="label">存储引擎</div></div>
<div class="stat-card"><div class="num">1980</div><div class="label">测试用例(+14 e2e</div></div>
<div class="stat-card"><div class="num">93.50%</div><div class="label">行覆盖率</div></div>
<div class="stat-card"><div class="num">63KB</div><div class="label">gzip 体积</div></div>
<div class="stat-card"><div class="num">4+3</div><div class="label">存储模式+后端</div></div>
<div class="stat-card"><div class="num">72</div><div class="label">SQL 关键字</div></div>
<div class="stat-card"><div class="num">76</div><div class="label">测试套件</div></div>
<div class="stat-card"><div class="num">91</div><div class="label">测试套件</div></div>
</div>
</div>
</section>