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:
+1
-1
@@ -591,7 +591,7 @@ DROP TABLE temp_logs;`,
|
||||
-- AriaEngine 特性:
|
||||
-- • LSM-Tree: MemTable (红黑树) → SSTable 多级索引
|
||||
-- • WAL: 分片文件 + 标准 CRC32 + 空洞检测 + 崩溃恢复(残缺 SSTable 自动跳过)
|
||||
-- • MVCC: 版本链 + 快照隔离
|
||||
-- • MVCC: 版本链仅作事务内 undo(无快照隔离;事务串行)
|
||||
-- • 页面化物理存储: SSTable 4KB 页面 + BufferPool LRU 缓存 (v0.5.0)
|
||||
-- • 完整性: 整文件 CRC-32 校验(数据腐坏自动清理自愈)
|
||||
-- • Bloom Filter: FNV-1a + Murmur 双哈希
|
||||
|
||||
+19
-5
@@ -140,7 +140,7 @@ npm install @metona-team/metona-sqlark</pre>
|
||||
<table>
|
||||
<tr><th>文件</th><th>格式</th><th>用途</th></tr>
|
||||
<tr><td><code>metona-sqlark.js</code></td><td>UMD</td><td>浏览器开发版(含 sourcemap)</td></tr>
|
||||
<tr><td><code>metona-sqlark.min.js</code></td><td>UMD (minified)</td><td>生产环境(~105KB / ~27KB gzip)</td></tr>
|
||||
<tr><td><code>metona-sqlark.min.js</code></td><td>UMD (minified)</td><td>生产环境(实测 251,109 字节 / gzip 63,145 字节,含全部引擎与 SQL 层)</td></tr>
|
||||
<tr><td><code>metona-sqlark.esm.js</code></td><td>ES Module</td><td>现代打包工具 / 浏览器 ESM</td></tr>
|
||||
<tr><td><code>metona-sqlark.cjs</code></td><td>CommonJS</td><td>Node.js require()</td></tr>
|
||||
<tr><td><code>metona-sqlark.d.ts</code></td><td>TypeScript 声明</td><td>类型提示</td></tr>
|
||||
@@ -793,7 +793,7 @@ db.<span class="f">broadcastChange</span>(<span class="s">'users'</span>);</pre>
|
||||
<tr><td><code>MemoryEngine</code></td><td>memory</td><td>❌</td><td>哈希</td><td>快照回滚</td><td>临时数据、缓存、测试</td></tr>
|
||||
<tr><td><code>KVStoreEngine</code> 🆕</td><td>disk</td><td>✅ KVStore(OPFS)</td><td>哈希</td><td>原子日志 flush</td><td>标准持久化(v0.6.0 替代 IndexedDB)</td></tr>
|
||||
<tr><td><code>HybridEngine</code></td><td>hybrid</td><td>✅ Write-Through</td><td>哈希</td><td>双引擎代理</td><td>生产推荐,读写均走内存</td></tr>
|
||||
<tr style="border-top:2px solid var(--primary);"><td><code style="color:#ec4899;font-weight:700;">AriaEngine</code></td><td>aria</td><td>✅ WAL + SSTable</td><td>LSM-Tree</td><td>MVCC 快照隔离</td><td>自研引擎:大表、高并发、需崩溃恢复</td></tr>
|
||||
<tr style="border-top:2px solid var(--primary);"><td><code style="color:#ec4899;font-weight:700;">AriaEngine</code></td><td>aria</td><td>✅ WAL + SSTable</td><td>LSM-Tree</td><td>版本链 undo + 事务串行</td><td>自研引擎:大表、高并发、需崩溃恢复</td></tr>
|
||||
</table>
|
||||
<p>✅ <strong>v0.6.0: IndexedDB 已完全移除</strong> — disk 模式改用自研 KVStore 引擎(多 key 原子写 + 快照/日志崩溃恢复),旧库可经 <code>migrateFromIndexedDB()</code> 一键迁移。</p>
|
||||
|
||||
@@ -820,7 +820,7 @@ db.<span class="f">broadcastChange</span>(<span class="s">'users'</span>);</pre>
|
||||
<table>
|
||||
<tr><th>特性</th><th>Memory</th><th>Disk (KVStore)</th><th>Hybrid</th><th>Aria</th></tr>
|
||||
<tr><td>持久化</td><td>❌</td><td>✅ KVStore(OPFS / 内存介质)</td><td>✅ 双写</td><td>✅ 后端决定</td></tr>
|
||||
<tr><td>事务</td><td>✅ 快照回滚</td><td>✅ 单日志记录原子写</td><td>✅ 双引擎(磁盘优先)</td><td>✅ MVCC 快照隔离</td></tr>
|
||||
<tr><td>事务</td><td>✅ 快照回滚</td><td>✅ 单日志记录原子写</td><td>✅ 双引擎(磁盘优先)</td><td>✅ 快照回滚(事务串行,非 MVCC 隔离)</td></tr>
|
||||
<tr><td>索引</td><td>Hash</td><td>Hash(重启恢复)</td><td>Hash</td><td>LSM 二级(重启恢复)</td></tr>
|
||||
<tr><td>上限</td><td>内存</td><td>磁盘可用</td><td>磁盘可用</td><td>内存</td></tr>
|
||||
<tr><td>多标签页锁</td><td>—</td><td>—</td><td>—</td><td>✅ Web Locks</td></tr>
|
||||
@@ -834,8 +834,8 @@ db.<span class="f">broadcastChange</span>(<span class="s">'users'</span>);</pre>
|
||||
<tr><td><strong>LSM-Tree 索引</strong></td><td>MemTable (红黑树) + 多级 SSTable,写优化,支持范围扫描</td></tr>
|
||||
<tr><td><strong>4KB 页面化物理存储</strong></td><td>SSTable 切分为 4KB 页面(FileManager 分配 pageId + BufferPool LRU 缓存 256 页 ≈ 1MB),save 即落盘,`SSTableMeta.pageIds` 持久化(旧整 value 数据兼容)✅ v0.5.0</td></tr>
|
||||
<tr><td><strong>Buffer Pool</strong></td><td>LRU 页面缓存,可控内存占用(默认 256 页 ≈ 1MB),脏页写回 ✅ v0.5.0 真实接入</td></tr>
|
||||
<tr><td><strong>WAL 日志</strong></td><td>分片文件(`__wal_%06d.bin`,4MB 阈值切换 + 真追加)· 标准 CRC32 记录校验 · full/batch/none 三种同步模式(full 真正同步)· 空洞检测截断 · 16MB 阈值自动 checkpoint ✅ v0.5.0</td></tr>
|
||||
<tr><td><strong>MVCC 事务</strong></td><td>快照隔离 (Snapshot Isolation),读写不互斥,版本链 + GC(引擎事务读取走 txnSnapshot,MVCC 版本链作 undo log)</td></tr>
|
||||
<tr><td><strong>WAL 日志</strong></td><td>分片文件(`__wal_%06d.bin`,4MB 阈值切换 + 真追加)· 标准 CRC32 记录校验(记录级 CRC 失败 → <code>droppedWALRecords</code> + 数据丢失标记,不再只打日志)· full/batch/none 三种同步模式(full 真正同步)· 空洞如实上报(内部空洞 → <code>ARIA_WAL_GAP</code> 且拒绝静默继续) · 16MB 阈值自动 checkpoint ✅ v0.8.0 补齐</td></tr>
|
||||
<tr><td><strong>MVCC 版本链</strong></td><td>版本链仅作事务内 undo(提交即清理,自动 GC),<strong>不提供快照隔离</strong>;引擎事务读取走 txnSnapshot,同一实例同时只允许一个事务(并发 begin 抛 TX_ACTIVE)✅ v0.8.0 如实描述</td></tr>
|
||||
<tr><td><strong>全库 AES-GCM 加密</strong></td><td>EncryptedBackend 透明加解密(WAL/SSTable/Schema/元数据全密文)· PBKDF2 密钥派生 + salt 持久化 + 密码验证 · 明文/加密库开关一致性检测 ✅ v0.5.0</td></tr>
|
||||
<tr><td><strong>多标签页独占锁</strong></td><td>Web Locks API:第二个标签页打开同一库抛 `ARIA_LOCKED`,open 失败自动释放 ✅ v0.5.0</td></tr>
|
||||
<tr><td><strong>Bloom Filter</strong></td><td>快速判定 key 不存在,减少无效磁盘 I/O,SSTableReader 二分查找统一 ✅ v0.2.5</td></tr>
|
||||
@@ -878,6 +878,7 @@ db.<span class="f">broadcastChange</span>(<span class="s">'users'</span>);</pre>
|
||||
<tr><td><code>walSizeThreshold</code></td><td><code>number</code></td><td><code>16777216</code></td><td>WAL 大小阈值(字节),超阈值触发 checkpoint ✅ v0.2.5</td></tr>
|
||||
<tr><td><code>compression</code></td><td><code>boolean</code></td><td><code>false</code></td><td>是否启用页面压缩</td></tr>
|
||||
<tr><td><code>storageBackend</code></td><td><code>'opfs'|'memory'|'kv'</code></td><td><code>'opfs'</code></td><td>存储后端类型('kv' = 自研 KVStore 后端 🆕 v0.6.1)</td></tr>
|
||||
<tr><td><code>maxMemoryMB</code></td><td><code>number</code></td><td><code>64</code></td><td>内存预算(MB):主 LSM 估算内存超限时触发 flush + MVCC GC</td></tr>
|
||||
<tr><td><code>encryption</code></td><td><code>{ password: string }</code></td><td>-</td><td>全库 AES-256-GCM 透明加密(PBKDF2 派生 + salt 持久化 + 密码验证)🆕 v0.5.0</td></tr>
|
||||
<tr><td><code>pageStorage</code></td><td><code>boolean</code></td><td>opfs/kv 自动启用</td><td>SSTable 4KB 页面化物理存储(BufferPool 缓存)🆕 v0.5.0</td></tr>
|
||||
</table>
|
||||
@@ -901,6 +902,7 @@ db.<span class="f">broadcastChange</span>(<span class="s">'users'</span>);</pre>
|
||||
<tr><td><code>TABLE_EXISTS</code></td><td>表已存在</td></tr>
|
||||
<tr><td><code>DUPLICATE_KEY</code></td><td>主键重复</td></tr>
|
||||
<tr><td><code>UNIQUE_VIOLATION</code></td><td>唯一约束冲突</td></tr>
|
||||
<tr><td><code>FOREIGN_KEY_VIOLATION</code></td><td>外键约束冲突:删除/更新被引用行时存在依赖行(含 RESTRICT / CASCADE 语义)</td></tr>
|
||||
<tr><td><code>VALIDATION_ERROR</code></td><td>数据校验失败</td></tr>
|
||||
<tr><td><code>TYPE_ERROR</code></td><td>字段类型错误</td></tr>
|
||||
<tr><td><code>SCHEMA_ERROR</code></td><td>表结构定义错误</td></tr>
|
||||
@@ -927,6 +929,18 @@ db.<span class="f">broadcastChange</span>(<span class="s">'users'</span>);</pre>
|
||||
<tr><td><code>ARIA_WAL_GAP</code></td><td>WAL 分片空洞(含前缀缺失):拒绝在"少了一段日志"的情况下静默继续 🆕 v0.8.0</td></tr>
|
||||
<tr><td><code>ARIA_WRITE_LOST</code></td><td>manifest 声称有未落盘数据,但 WAL 中没有任何可重放的记录(已确认写入确实丢失)🆕 v0.8.0</td></tr>
|
||||
<tr><td><code>STALE_INSTANCE</code></td><td>陈旧实例拒绝提交(另一个实例已提交更新的世代),不会静默覆盖 🆕 v0.8.0</td></tr>
|
||||
<tr><td><code>ARIA_MANIFEST_NOT_LOADED</code></td><td>未 <code>load()</code> 就提交 manifest:拒绝写坏介质 🆕 v0.8.0</td></tr>
|
||||
<tr><td><code>ARIA_SSTABLE_SAVE_CONTRACT</code></td><td>存储实现违反 <code>save()</code> 契约(既不抛错也不返回结果):不把"写调用返回了"当成落盘成功 🆕 v0.8.0</td></tr>
|
||||
<tr><td><code>ARIA_DB_NOT_OPEN</code></td><td>在 <code>open()</code> 之前使用 Aria 存储层(如加密后端) 🆕 v0.8.0</td></tr>
|
||||
<tr><td><code>ARIA_OPEN_ERROR</code></td><td>AriaEngine 打开失败(底层错误作为 cause 保留) 🆕 v0.8.0</td></tr>
|
||||
<tr><td><code>DB_NOT_OPEN</code></td><td>数据库未打开(KVStoreEngine 操作前未 <code>open()</code>)</td></tr>
|
||||
<tr><td><code>KV_SCHEMA_ERROR</code></td><td>KVStore 中的表结构记录损坏</td></tr>
|
||||
<tr><td><code>KV_LOG_ERROR</code> / <code>KV_BACKGROUND_ERROR</code></td><td>KVStore 日志写入失败 / 后台刷盘失败</td></tr>
|
||||
<tr><td><code>TX_NONE</code></td><td>没有活跃事务时执行 COMMIT / ROLLBACK</td></tr>
|
||||
<tr><td><code>TX_ACTIVE</code></td><td>已有事务进行中时再次 BEGIN</td></tr>
|
||||
<tr><td><code>TX_COMMIT_ERROR</code></td><td>hybrid 模式提交失败:磁盘侧已提交、内存侧失败(错误信息说明磁盘数据已落盘)</td></tr>
|
||||
<tr><td><code>UNKNOWN_STATEMENT</code></td><td>执行器遇到未知语句类型</td></tr>
|
||||
<tr><td><code>COLUMN_EXISTS</code></td><td>ADD COLUMN 的列已存在</td></tr>
|
||||
</table>
|
||||
|
||||
</main>
|
||||
|
||||
+10
-10
@@ -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-shakable,UMD/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>
|
||||
|
||||
Reference in New Issue
Block a user