release: v0.5.1 — 存储后端生产级硬化(CRC-32/全库加密/WAL分片/页面化存储/多标签页锁/e2e)+ 深度审查修复(假实现接线/死代码清理)
CI / test (18.x) (push) Successful in 10m10s
CI / test (20.x) (push) Successful in 10m10s
CI / test (22.x) (push) Successful in 10m6s
CI / e2e (push) Successful in 9m51s
CI / test (24.x) (push) Successful in 10m28s

This commit is contained in:
thzxx
2026-08-10 12:07:00 +08:00
parent cff98b0903
commit 334067d89e
88 changed files with 15713 additions and 10626 deletions
+83 -50
View File
@@ -1,21 +1,22 @@
# MetonaSqlark
<p align="center">
<img src="https://img.shields.io/badge/version-0.4.4-blue?style=flat-square" alt="version">
<img src="https://img.shields.io/badge/version-0.5.0-blue?style=flat-square" alt="version">
<img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="license">
<img src="https://img.shields.io/badge/coverage-84.2%25-brightgreen?style=flat-square" alt="coverage">
<img src="https://img.shields.io/badge/tests-958%20passed-success?style=flat-square" alt="tests">
<img src="https://img.shields.io/badge/coverage-86.7%25-brightgreen?style=flat-square" alt="coverage">
<img src="https://img.shields.io/badge/tests-1022%20passed-success?style=flat-square" alt="tests">
</p>
> 基于 TypeScript 的**前端关系型数据库**,支持完整 SQL 查询、Query Builder 链式 API、与 **AriaEngine 自研页面式存储引擎**。
> 基于 TypeScript 的**前端关系型数据库**,支持完整 SQL 查询、Query Builder 链式 API、与 **AriaEngine 自研存储引擎**。
---
## ✨ 特性
- 🚀 **AriaEngine 自研存储引擎** — LSM-Tree 页面式存储,4KB Slotted Page、WAL 崩溃恢复(full 模式真正同步)、LZ4 压缩
- 💾 **OPFS 自研存储后端** — 纯浏览器文件系统,零 IndexedDB 依赖,二进制页面文件,schema 持久化(空表/索引跨重启完整保留)
- 🔒 **生产级数据安全** — WAL 原子写入 + CRC 完整性校验、`RESTRICT` 外键约束、崩溃恢复自愈`repair()` 无需删库重建)、SQL 注入防护
- 🚀 **AriaEngine 自研存储引擎** — LSM-Tree + **SSTable 4KB 页面化物理存储**BufferPool LRU 缓存 + FileManager 页面管理,v0.5.0 真实接入)、WAL 分片文件(真追加 + 空洞检测)、LZ4 压缩
- 💾 **OPFS 自研存储后端** — 纯浏览器文件系统,零 IndexedDB 依赖,二进制页面文件,schema 持久化,单文件 COW 原子写,崩溃残留自动清理
- 🔒 **生产级数据安全** — WAL 原子写入 + 标准 CRC32 完整性校验SSTable 整文件校验 + WAL 记录校验)、**全库 AES-256-GCM 透明加密**PBKDF2 密钥派生 + salt 持久化 + 密码验证,v0.5.0 真实接线)`RESTRICT` 外键约束、崩溃恢复自愈
- 🔐 **多标签页独占锁** — Web Locks API,第二个标签页打开同一库抛 `ARIA_LOCKED`v0.5.0
- 🛡 **输入校验全覆盖**`maxLength`/`min`/`max` 约束、类型检查、必填验证
- 💾 **多引擎架构** — Memory / IndexedDB / OPFS / Hybrid(write-through) / Aria 五种模式
- 📝 **完整 SQL 支持** — SELECT/JOIN/子查询/GROUP BY/HAVING/ORDER BY/LIMIT/BETWEEN/IF NOT EXISTS/ALTER TABLE/TRUNCATE TABLE/UNION/INSERT...SELECT/事务语句/CREATE INDEX/EXISTSv0.3.0+ CASE WHEN/哈希连接/组提交(v0.3.1+ 多标签页同步(v0.3.2
@@ -24,13 +25,13 @@
- 🔗 **Query Builder API** — 链式 `.select().where().orderBy().limit().execute()`
- 🔄 **事务回滚** — Memory/IndexedDB/Hybrid/Aria 四引擎事务原子性,自动回滚,MVCC 版本链接入读写路径
- 🔗 **外键级联** — ON DELETE + ON UPDATECASCADE / SET NULL / RESTRICT)全引擎支持,支持更新主键(v0.4.2)
- 🛡 **崩溃恢复自愈** — 残缺 SSTable 打开自动跳过、`db.repair()` 自愈、`db.clearAll()` 重置、迁移版本持久化(v0.4.2)
- 🧵 **关闭时序与后台任务加固** — 后台 flush/compaction 串行入队(close 排空后才关闭存储,失败显式报告 `ARIA_BACKGROUND_ERROR`、预加载等待链稳定(compaction 竞态修复)、事务提交先落 WAL 再合并快照(v0.4.3)
- 📏 **SSTable 编码修复** — 块大小按 UTF-8 字节精确计算(大段中文内容不再因缓冲区低估崩溃)、长度字段 u32(>64KB value 不截断)、v1/v2 双格式兼容(旧库数据不丢)(v0.4.4
- 🛡 **崩溃恢复自愈** — 残缺 SSTable 打开自动跳过、`db.repair()` 自愈(含孤儿页面清理与残留清理)`db.clearAll()` 重置、迁移版本持久化(v0.4.2/v0.5.0
- 🧵 **关闭时序与后台任务加固** — 后台 flush/compaction 串行入队(close 排空后才关闭存储,失败显式报告 `ARIA_BACKGROUND_ERROR`
- 📏 **SSTable 编码修复** — 块大小按 UTF-8 字节精确计算、长度字段 u32、v1/v2 双格式兼容(v0.4.4
- 🌲 **RB-Tree 完整实现** — 标准红黑树插入+删除修复,O(log n) 保证
-**性能优化** — SSTableReader 二分查找统一、IndexedDB 索引利用、crypto 实例化避免全局状态
- 🌐 **浏览器兼容** — Chrome 80+ / Firefox 80+ / Safari 14+ / Edge 80+ / Node.js 16+
- 🧪 **958 测试 · 84.2% 覆盖率**52 套件,生产级质量保证
- 🧪 **1022 测试 · 86.7% 覆盖率**62 套件 + 7 个 Playwright 真实 Chromium e2e,生产级质量保证
---
@@ -217,6 +218,7 @@ await db2.disconnect(); // 引用计数 -1
| `maxRowsPerQuery` | `number` | `0` | 查询结果行数上限(0=不限制)✅ v0.2.5 生效 |
| `debug` | `boolean` | `false` | 调试模式,输出详细日志 🆕 |
| `onError` | `(error) => void` | — | 全局错误回调 🆕 |
| `aria` | `AriaEngineConfig` | — | AriaEngine 专属配置透传:`walSyncMode`/`checkpointInterval`/`encryption`/`pageStorage`/`compression` 等 🆕 v0.5.0 |
### ColumnDef 列定义
@@ -254,9 +256,24 @@ await db2.disconnect(); // 引用计数 -1
| `db.clearAll()` | 清空全部数据与表结构(保留库本身,实例可继续使用)🆕 v0.4.2 |
| `db.exportTable(name)` / `db.exportAll()` | 导出数据 JSON |
| `db.importTable(name, data)` | 导入数据 |
| `db.backup()` | 在线备份:全库一致性快照(Aria 引擎级备份 / 其余引擎回退 exportAll)🆕 v0.5.1 |
| `db.addMigration(v, fn)` / `db.migrateTo(v)` | 数据迁移(版本持久化到库内,重启不重跑)🆕 v0.4.2 |
| `db.subscribe(table, fn)` | 订阅表变更 |
| `db.on(hook, fn)` | 注册钩子 14 种 |
| `db.on(hook, fn)` | 注册钩子 14 种,全部真实触发)🆕 v0.5.1 |
### 维护语句(v0.5.1 SQL 入口)
| 语句 | 说明 | 引擎支持 |
|------|------|----------|
| `EXPLAIN SELECT ...` | 输出查询计划(type/table/where/estimatedRows/actualTimeMs | 全部 |
| `ANALYZE [TABLE] name` | 收集表统计信息(行数/平均行大小/索引深度/列基数) | Aria |
| `REINDEX [TABLE] name` | 重建表二级索引 | Aria |
| `VACUUM` | 压缩 LSM + 清理 MVCC 碎片 | Aria |
| `SAVEPOINT name` | 创建嵌套事务保存点 | Aria |
| `ROLLBACK TO [SAVEPOINT] name` | 回滚到保存点 | Aria |
| `RELEASE [SAVEPOINT] name` | 释放保存点 | Aria |
> 不支持的引擎执行维护语句抛 `NOT_SUPPORTED`。
### 连接池(v0.1.13
@@ -289,10 +306,12 @@ const { data, loading, refresh } = useSqlarkQuery(db, 'SELECT * FROM users');
| **事务回滚** | ✅ 快照 | ✅ 原子flush | ✅ 快照 | ✅ 双引擎 | ✅ MVCC |
| **二级索引** | ✅ Hash | ✅ Hash | ✅ Hash(重启恢复) | ✅ Hash | ✅ LSM(重启恢复) |
| **查询性能** | ⚡ O(1) PK | 🟡 O(1) PK | 🟡 O(1) PK | ⚡ O(1) PK | ⚡ O(log n) |
| **数据上限** | 内存限制 | ~2GB(IDB限制) | ~磁盘可用(整表重写,≤1000行/表为宜 | ~2GB(IDB) | 内存限制 |
| **浏览器** | 全部 | 全部 | Chrome/Edge 102+ | 全部 | 全部 |
| **适用场景** | 缓存/测试 | 标准持久化 | Chromium专有 | 速度+持久化 | 大规模/分析 |
| **测试覆盖** | 30+ | 30+ | 15 | 15+ | 200+ |
| **数据上限** | 内存限制 | ~2GB(IDB限制) | ~磁盘可用(页面化后大表可行 | ~2GB(IDB) | 内存限制 |
| **浏览器** | 全部 | 全部 | Chrome/Edge 102+ / Firefox 111+ / Safari 15.2+ | 全部 | 全部 |
| **多标签页** | — | ✅ | ❌(Web Locks 独占锁,v0.5.0 | ❌ | ✅ 锁保护 |
| **全库加密** | — | — | | — | ✅ AES-GCMv0.5.0 |
| **适用场景** | 缓存/测试 | 标准持久化 | Chromium+ 持久化 | 速度+持久化 | 大规模/分析 |
| **测试覆盖** | 30+ | 30+ | 15+e2e | 15+ | 400+ |
### Memory 模式
- **环境**: 所有浏览器、Node.js
@@ -307,10 +326,10 @@ const { data, loading, refresh } = useSqlarkQuery(db, 'SELECT * FROM users');
- **适用**: 标准前端数据库持久化场景
### Disk (OPFS) 模式
- **环境**: **仅限** Chrome 102+ / Edge 102+Origin Private File System
- **限制**: Firefox/Safari 不支持 OPFS API每次写入重写整表 JSON 文件(大表性能差,不建议 >1000 行)
- **环境**: Chrome 102+ / Edge 102+ / Firefox 111+ / Safari 15.2+Origin Private File System
- **限制**: 每次写入重写整表 JSON 文件(大表性能差,不建议 >1000 行);多标签页并发写无保护(Hybrid 模式)
- **能力**: 完整 CRUD、重启自动加载数据(空表/索引/schema 完整保留,v0.4.2)、事务回滚、并发写安全(内存快照一致)
- **适用**: Chromium 独占场景、小数据集持久化
- **适用**: 小数据集持久化(大表请用 Aria + OPFS 页面化存储)
### Hybrid 模式
- **环境**: 所有浏览器
@@ -320,8 +339,8 @@ const { data, loading, refresh } = useSqlarkQuery(db, 'SELECT * FROM users');
### Aria 模式
- **环境**: 所有浏览器(后端可选 IndexedDB / OPFS / Memory
- **限制**: Memory 后端重启丢失;IndexedDB 后端受配额限制;OPFS 后端仅 Chromium
- **能力**: LSM-Tree 存储引擎、二级索引(跨重启恢复)、MVCC 事务、WAL 原子写入崩溃恢复(残缺 SSTable 打开自动跳过)、ON UPDATE/DELETE 外键级联、Bloom Filter、AES-GCM 加密、Savepoint、EXPLAIN、ANALYZE、REINDEX、VACUUM、`repair()` 自愈
- **限制**: Memory 后端重启丢失;IndexedDB 后端受配额限制;OPFS 后端需 OPFS 支持(Chrome 102+ / Firefox 111+ / Safari 15.2+);同一库仅允许一个标签页打开(Web Locks 独占锁,第二个标签页抛 `ARIA_LOCKED`
- **能力**: LSM-Tree 存储引擎、SSTable 4KB 页面化物理存储(OPFS 后端默认启用,BufferPool LRU 缓存)、二级索引(跨重启恢复)、MVCC 事务、WAL 分片文件原子写入崩溃恢复(真追加 + 空洞检测)、ON UPDATE/DELETE 外键级联、Bloom Filter、**全库 AES-GCM 透明加密**v0.5.0)、LZ4 压缩、Savepoint、EXPLAIN、ANALYZE、REINDEX、VACUUM、`repair()` 自愈(含孤儿页面清理)
- **适用**: 大规模数据分析、需要自研引擎可控性的高级场景
---
@@ -331,11 +350,15 @@ const { data, loading, refresh } = useSqlarkQuery(db, 'SELECT * FROM users');
AriaEngine 是内置的页面式存储引擎,对标 SQLite 的设计理念:
```typescript
// 激活 AriaEngine
// 激活 AriaEngine(全库加密 + OPFS 页面化存储,v0.5.0
const db = await MetonaSqlark.create({
name: 'my-app',
mode: 'aria', // 🆕 自研引擎模式
diskEngine: 'indexeddb', // 'indexeddb' | 'opfs' | 'memory'
diskEngine: 'opfs', // 'indexeddb' | 'opfs' | 'memory'
aria: { // 🆕 AriaEngine 专属配置透传(v0.5.0
walSyncMode: 'full', // WAL 同步模式
encryption: { password: 'my-password' }, // 全库 AES-GCM 加密
},
});
// 与现有 API 完全兼容
@@ -351,36 +374,39 @@ const rows = await db.query('SELECT * FROM users');
```
┌──────────────────────────────────────────┐
│ AriaEngine v0.2.5
│ AriaEngine v0.5.0
│ (implements IStorageEngine) │
├──────────────────────────────────────────┤
│ LSM-Tree │ Buffer Pool │ WAL │
│ MemTable │ LRU (256pp) │ Recovery
│ +SSTable │ +FileManager │ +CRC
│ MemTable │ LRU (256pp) │ Segmented
│ +SSTable │ +FileManager │ +CRC-32
│ (4KB Page) │ │ +空洞检测 │
├──────────────────────────────────────────┤
│ MVCC │ Bloom Filter │ LZ4
│ Snapshot │ FNV-1a+Murmur│ Compress
│ +Savepoint│ +Serialize │ │
│ MVCC │ Bloom Filter │ LZ4 v2
│ Snapshot │ FNV-1a+Murmur│ +大小头
│ +Savepoint│ +Serialize │ │
├──────────────────────────────────────────┤
AES-GCM │ 二级索引 ANALYZE
Encrypt │ Per-Column EXPLAIN
EncryptedBackend │ 二级索引 ANALYZE │
AES-GCM 全库 │ Per-Column EXPLAIN │
├──────────────────────────────────────────┤
Storage Backend (IDB / OPFS / Memory) │
Web Locks 独占锁 │ OPFS Backend (v2) │
│ (多标签页) │ append/COW原子/清理 │
└──────────────────────────────────────────┘
```
| 特性 | 说明 |
|------|------|
| **LSM-Tree** | MemTable (红黑树) → SSTable 多级索引,异步 Compaction(从存储兜底加载,不依赖缓存),写背压 |
| **WAL** | Write-Ahead Log 二进制格式,CRC 校验,记录与计数单事务原子写入,full/batch/none 三种模式(full 模式真正同步 ✅ v0.2.5),16MB 阈值自动 checkpoint(活跃事务期间不截断 ✅ v0.4.2) |
| **崩溃恢复** | 打开时完整性校验(残缺 SSTable 自动跳过并清理)、WAL 按 key 扫描恢复(不丢记录)、恢复后自动重建二级索引 ✅ v0.4.2 |
| **页面化物理存储** | OPFS 后端下 SSTable 存为 4KB 页面(`pageStorage` 自动启用):FileManager 分配 pageId、BufferPool LRU 缓存(256 页 ≈ 1MB 可控内存)、save 即落盘(WAL checkpoint 截断安全)、`SSTableMeta.pageIds` 持久化(旧整 value 数据兼容)v0.5.0 |
| **WAL** | 分片文件 `__wal_%06d.bin`(4MB 阈值切换)+ 真追加(createWritable keepExistingData);标准 CRC32 记录校验;full/batch/none 三种模式(full 真正同步);空洞检测截断;16MB 阈值自动 checkpoint(活跃事务期间不截断)v0.5.0 |
| **崩溃恢复** | 打开时完整性校验(残缺 SSTable 自动跳过并清理 + 整文件 CRC-32 校验)、WAL 按 key 扫描恢复(不丢记录)、恢复后自动重建二级索引 |
| **全库加密** | `encryption.password` 配置 → EncryptedBackend 透明加解密(WAL/SSTable/Schema/元数据全密文);PBKDF2-SHA256 密钥派生 + salt/verifier 持久化;密码错误/数据篡改 → `ARIA_DECRYPT_ERROR`;明文库/加密库开关一致性检测 v0.5.0 |
| **MVCC** | 版本链 + 快照隔离,事务读写不互斥,提交/回滚按事务写入 key 精准清理,自动 GC |
| **Buffer Pool** | SSTable 缓存 LRU 上限(`bufferPoolPages` × `pageSize`,默认 256 页 ≈ 1MB 可控内存)✅ v0.2.6 生效,查询前异步预加载兜底,缓存驱逐不丢数据 |
| **Bloom Filter** | FNV-1a + Murmur 双哈希,SSTable footer 序列化,查询时 probe |
| **二级索引** | 每列独立 LSM Tree,支持 $eq/$in/$gt/$lt 范围扫描,跨重启自动恢复,WAL 恢复后自动重建 ✅ v0.4.2 |
| **AES-GCM** | PBKDF2 密钥派生 + AES-256-GCM 页面级加密,CryptoManager 实例化 ✅ v0.2.5 |
| **Compaction** | 异步 Leveled CompactionLevel 0 > 8 触发同步背压,compactLevel public 接口 ✅ v0.2.5 |
| **OPFS Backend** | 纯浏览器文件系统,Promise 队列串行写,零外部依赖 |
| **二级索引** | 每列独立 LSM Tree,支持 $eq/$in/$gt/$lt 范围扫描,跨重启自动恢复,WAL 恢复后自动重建 |
| **多标签页锁** | Web Locks API 库级独占锁(`ifAvailable` 不排队):第二个标签页抛 `ARIA_LOCKED`;不支持的环境降级无锁并告警 v0.5.0 |
| **Compaction** | 异步 Leveled CompactionLevel 0 > 8 触发同步背压,compactLevel public 接口 |
| **OPFS Backend** | 零外部依赖纯浏览器文件系统:单文件 COW 原子写、`append` 真追加、写队列失败不中断、close 排空、崩溃残留自动清理 v0.5.0 |
---
@@ -391,6 +417,7 @@ npm install # 安装依赖
npm run dev # 开发模式(localhost:3001
npm run build # 生产构建(生成 dist/
npm test # 运行测试
npm run test:e2e # Playwright e2e(真实 Chromium + OPFS,需先 build
npm run lint # 代码检查
npm run typecheck # 类型检查
```
@@ -401,23 +428,29 @@ npm run typecheck # 类型检查
| 指标 | 数值 |
|------|------|
| 测试用例 | 958 |
| 测试套件 | 51 |
| 行覆盖率 | 84.2% |
| 测试用例 | 1022 |
| 测试套件 | 62+7 Playwright e2e |
| 行覆盖率 | 86.7% |
| SQL 关键字 | 36 |
| 存储引擎 | 5Memory / IndexedDB / OPFS / Hybrid / **Aria** |
### 🌐 浏览器兼容性
| 浏览器 | 最低版本 | Memory | IndexedDB | OPFS | Aria |
|--------|----------|--------|-----------|------|------|
| Chrome | 80+ | ✅ | ✅ | ✅ (102+) | ✅ |
| Firefox | 80+ | ✅ | ✅ | | ✅ |
| Safari | 14+ | ✅ | ✅ | | ✅ |
| Edge | 80+ | ✅ | ✅ | ✅ (102+) | ✅ |
| Node.js | 16+ | ✅ | ✅ (fake-idb) | ❌ | ✅ |
| 浏览器 | 最低版本 | Memory | IndexedDB | OPFS | Web Locks | Aria |
|--------|----------|--------|-----------|------|-----------|------|
| Chrome | 80+ | ✅ | ✅ | ✅ (102+) | ✅ (69+) | ✅ |
| Firefox | 80+ | ✅ | ✅ | ✅ (111+) | ✅ (96+) | ✅ |
| Safari | 14+ | ✅ | ✅ | ✅ (15.2+) | ✅ (15.4+) | ✅ |
| Edge | 80+ | ✅ | ✅ | ✅ (102+) | ✅ (79+) | ✅ |
| Node.js | 16+ | ✅ | ✅ (fake-idb) | ❌ (测试用 mock) | ❌ | ✅ |
> **注意**: OPFS 模式仅限 Chromium 内核浏览器 (Chrome/Edge 102+)Firefox/Safari 请使用 `diskEngine: 'indexeddb'`。
> **OPFS 支持说明(v0.4.5 更新)**: ChromiumChrome/Edge 102+)、Firefox 111+、Safari 15.2+
> 均已支持基础 OPFS API`createWritable` 原子写)。OPFS 无跨文件事务,
> AriaEngine 以 WAL 分片单文件原子写 + 空洞检测截断保证崩溃一致性。
>
> **多标签页保护(v0.4.5**: AriaEngine 打开库时通过 Web Locks API 获取库级独占锁,
> 第二个标签页打开同一库会抛 `ARIA_LOCKED`。Web Locks 不可用的环境降级为无锁并告警
> (仅理论上,现代浏览器均支持)。
---