release: v0.6.0 — 完全移除 IndexedDB,自研 KVStore 事务存储引擎(多key原子写/快照日志恢复/CRC自愈)+ KVStoreEngine + 旧库迁移工具 + 10万级压力验证 + 崩溃注入e2e
CI / test (20.x) (push) Successful in 10m53s
CI / test (22.x) (push) Successful in 10m47s
CI / test (24.x) (push) Successful in 10m42s
CI / e2e (push) Successful in 10m27s
CI / test (18.x) (push) Successful in 11m1s

This commit is contained in:
thzxx
2026-08-10 13:56:04 +08:00
parent 2668bd47cf
commit 24b3ca1079
74 changed files with 8016 additions and 7688 deletions
+77 -159
View File
@@ -18,7 +18,7 @@ interface AriaEngineConfig {
/** 是否启用页面压缩(默认 false) */
compression?: boolean;
/** 存储后端 */
storageBackend?: 'indexeddb' | 'opfs' | 'memory';
storageBackend?: 'opfs' | 'memory';
/** WAL 大小阈值(字节,超过则强制 checkpoint,默认 16MB */
walSizeThreshold?: number;
/** 最大内存预算(MB,默认 64) */
@@ -45,8 +45,8 @@ interface AriaEngineConfig {
*/
/** 存储模式 */
type StorageMode = 'memory' | 'disk' | 'hybrid' | 'aria';
/** 磁盘引擎类型 */
type DiskEngine = 'indexeddb' | 'opfs';
/** 磁盘引擎类型v0.6.0: IndexedDB 已移除,'memory' 供 aria 内存后端) */
type DiskEngine = 'opfs' | 'memory';
/** 字段数据类型 */
type FieldType = 'string' | 'number' | 'boolean' | 'date' | 'json';
/** 列定义 */
@@ -164,7 +164,7 @@ interface MetonaPlugin {
/** 销毁 */
destroy(): void;
}
declare const VERSION = "0.5.1";
declare const VERSION = "0.6.0";
/**
* metona-sqlark Plugin — 插件系统
@@ -832,118 +832,75 @@ declare class MemoryEngine implements IStorageEngine {
private cascadeDelete;
}
declare class IndexedDBEngine implements IStorageEngine {
readonly name = "indexeddb";
private db;
private dbName;
private version;
private memoryCache;
private txActive;
open(dbName: string, version: number): Promise<void>;
/** v0.4.2-fix: 多标签页冲突处理 — 其他标签页升级版本时自动关闭当前连接 */
private setupVersionChangeHandler;
/**
* v0.4.2-fix: 打开 IndexedDB 连接。
* - P0-2: 请求版本低于库实际版本(VersionError)时,先无版本参数探测库当前版本,
* 再以实际版本重开(建表每张表版本号 +1,config.version 会过期)
* - P2-8: onblocked 为瞬时状态(另一连接短暂持有),等待后重试多次,超时才抛 IDB_BLOCKED
*/
private openDatabaseWithRetry;
/**
* 发起一次 indexedDB.open 请求(success/error/blocked 三态收敛)。
* onblocked 不立即失败:阻塞解除后 success 仍会触发,仅超时兜底判失败,
* 避免"拒绝后连接迟到成功"泄漏未关闭的数据库连接。
*/
private openRequest;
/** 无版本参数打开库,解析其当前实际版本号(随后立即关闭) */
private resolveCurrentVersion;
/**
* v0.4.2-fix (P2-7): 确保 __metona_schema store 存在。
* 新库(或版本升级前创建的旧库)没有该 store 时,通过一次版本升级创建,
* 使 getMeta/setMeta(迁移版本持久化)始终可用。
*/
private ensureSchemaStore;
/**
* 从 IDB 恢复内存 schema
* 1. 优先读取持久化的 schema 记录('__metona_schema' storev0.3.2
* 2. 旧数据回退:从 objectStore 主键 / 索引 / 样例数据推断
*/
private rebuildSchemaFromIDB;
/**
* AriaEngine Storage Backend — 存储后端抽象层
* @module engine/aria/store/backend
*
* 封装底层浏览器存储 APIIndexedDB / OPFS / Memory 回退),
* 供 Buffer Pool 的 PageIO 和 WAL 的 WALStore 使用。
*/
interface IStorageBackend {
/** 打开存储 */
open(name: string): Promise<void>;
/** 关闭存储 */
close(): Promise<void>;
/**
* v0.4.2-fix: 自愈 — 从磁盘重建内存 schema 与数据(schema 丢失/内存不一致时调用)。
* 无删库需求即可恢复可用的库。
*/
repair(): Promise<void>;
/**
* v0.4.2-fix: 清空全部数据与表结构(含持久化 schema 记录),保留库本身。
* 单个版本升级事务内原子完成。
*/
clearAll(): Promise<void>;
getMeta(key: string): Promise<string | null>;
setMeta(key: string, value: string): Promise<void>;
/** 是否已打开 */
isOpen(): boolean;
createTable(schema: TableSchema): Promise<void>;
dropTable(tableName: string): Promise<void>;
hasTable(tableName: string): Promise<boolean>;
getTableNames(): Promise<string[]>;
getTableSchema(tableName: string): Promise<TableSchema | null>;
/** 读取数据块 */
read(key: string): Promise<ArrayBuffer | null>;
/** 写入数据块 */
write(key: string, data: ArrayBuffer): Promise<void>;
/**
* v0.4.2-fix: 引擎级 ALTER TABLE — schema 持久化到 __metona_schema store
* 重启后 ALTER 不丢失(此前通用路径只改内存引用,重启回退;DROP 的行数据也没真正删)。
* 追加写入(v0.4.5 WAL 分片用,可选):
* - OPFS 后端实现真追加(createWritable keepExistingData + seekO(chunk)
* - 未实现的后端由调用方回退 read+writeEncryptedBackend 包装时整体重写保正确性)
* 语义:在 key 现有内容末尾追加 data;key 不存在时等同 write。
*/
alterTable(tableName: string, action: 'ADD' | 'DROP', column: ColumnDef & {
name: string;
}): Promise<void>;
insert(tableName: string, rows: Record<string, unknown>[]): Promise<string[]>;
find(tableName: string, query: QueryPlan): Promise<Record<string, unknown>[]>;
/** v0.4.0: 流式查询 — IDB 批量读入后逐行回调(保持接口一致性) */
findStream(tableName: string, query: QueryPlan, onRow: (row: Record<string, unknown>) => void): Promise<number>;
update(tableName: string, query: QueryPlan, updates: Record<string, unknown>): Promise<number>;
delete(tableName: string, query: QueryPlan): Promise<number>;
count(tableName: string, query?: QueryPlan): Promise<number>;
clear(tableName: string): Promise<void>;
createIndex(tableName: string, column: string, unique?: boolean): Promise<void>;
dropIndex(tableName: string, column: string, _indexName?: string): Promise<void>;
beginTransaction(): Promise<void>;
commitTransaction(): Promise<void>;
rollbackTransaction(): Promise<void>;
private idbCreateTable;
private idbDropTable;
private idbInsert;
private idbFind;
/** 尝试使用 IDB 索引进行等值查询,成功返回结果,不适用返回 null */
private tryIDBIndexLookup;
private idbUpdate;
private idbDelete;
private idbClear;
/** 持久化单个表 schema 到 __metona_schema storev0.4.2-fix: ALTER TABLE 用) */
private persistSchema;
/** 将内存缓存中的所有表数据原子性刷新到 IndexedDB */
private flushToIDB;
private ensureDB;
append?(key: string, data: ArrayBuffer): Promise<void>;
/**
* 批量原子写入(v0.4.2-fix):多个 key 在单个底层事务中提交,
* 中断时整体回滚,不留半写状态。WAL count 与记录同事务保证一致性。
*/
writeMany(entries: Record<string, ArrayBuffer>): Promise<void>;
/** 删除数据块 */
delete(key: string): Promise<void>;
/**
* 批量原子删除(v0.4.2-fix):多个 key 在单个底层事务中提交。
*/
deleteMany(keys: string[]): Promise<void>;
/** 列出所有 key */
listKeys(): Promise<string[]>;
/** 检查 key 是否存在 */
exists(key: string): Promise<boolean>;
/** 清空所有数据 */
clear(): Promise<void>;
}
declare class OPFSEngine implements IStorageEngine {
readonly name = "opfs";
private root;
private tablesDir;
declare class KVStoreEngine implements IStorageEngine {
readonly name = "kv";
private kv;
private memory;
private dbName;
private memoryCache;
/**
* v0.4.3-fix: 写操作串行队列 — 内存写 + 快照 + 文件持久化整体排队执行,
* close() 等待队列排空后再释放目录句柄(避免 close 后挂起写泄漏/读旧数据)。
* 前一个操作失败不阻塞后续(错误仍返回给调用方)。
*/
private opQueue;
/** 将写操作加入串行队列(快照在队列内取,始终最新) */
private enqueueOp;
private version;
private opened;
/** 活跃事务标记 */
private txActive;
/** 事务中写过的表(commit 时只 flush 这些表) */
private txDirtyTables;
constructor(medium?: IStorageBackend, checkpointThreshold?: number);
private rowKey;
private rowPrefix;
open(dbName: string, version: number): Promise<void>;
close(): Promise<void>;
isOpen(): boolean;
/** 自愈:重置内存缓存后从 OPFS 重新加载(单文件损坏不影响其他表) */
/**
* v0.6.0: 从 KVStore 重新加载全部数据到内存(多标签页同步重载用)。
* Hybrid 引擎的 reloadMemoryFromDisk 依赖磁盘引擎"读穿透"
* KVStoreEngine 读内存 → 提供 reload 重新加载磁盘最新数据。
*/
reload(): Promise<void>;
/** v0.4.2-fix: 自愈 — 校验 KVStore 日志/快照完整性并重建内存 */
repair(): Promise<void>;
/** 清空全部数据与表结构(删除目录内全部文件) */
clearAll(): Promise<void>;
getMeta(key: string): Promise<string | null>;
setMeta(key: string, value: string): Promise<void>;
@@ -952,13 +909,11 @@ declare class OPFSEngine implements IStorageEngine {
hasTable(tableName: string): Promise<boolean>;
getTableNames(): Promise<string[]>;
getTableSchema(tableName: string): Promise<TableSchema | null>;
/** v0.4.2-fix: 引擎级 ALTER TABLE — 内存 + schema 持久化 + 整表文件重写 */
alterTable(tableName: string, action: 'ADD' | 'DROP', column: ColumnDef & {
name: string;
}): Promise<void>;
insert(tableName: string, rows: Record<string, unknown>[]): Promise<string[]>;
find(tableName: string, query: QueryPlan): Promise<Record<string, unknown>[]>;
/** v0.4.0: 流式查询(委托内存缓存) */
findStream(tableName: string, query: QueryPlan, onRow: (row: Record<string, unknown>) => void): Promise<number>;
update(tableName: string, query: QueryPlan, updates: Record<string, unknown>): Promise<number>;
delete(tableName: string, query: QueryPlan): Promise<number>;
@@ -969,15 +924,22 @@ declare class OPFSEngine implements IStorageEngine {
beginTransaction(): Promise<void>;
commitTransaction(): Promise<void>;
rollbackTransaction(): Promise<void>;
private ensureDir;
private writeTableData;
private readTableData;
private ensureOpen;
private getPK;
/** 收集匹配查询的内存行主键(持久化差异计算用) */
private collectMatchingPks;
/**
* 从 OPFS 加载已有表到内存缓存
* v0.4.2-fix: 优先从持久化 schema__metona_schema_*.meta)恢复 —
* 空表不再消失、索引标记/主键/约束完整;无 schema 记录的旧库从数据推断(兼容)。
* 计算外键级联影响的表集合(传递闭包:A 被 B 引用,B 被 C 引用 → {A, B, C}
* 级联操作(delete/update 主键)需要把这些表一并重写持久化。
*/
private loadExistingTables;
private affectedTables;
/** 持久化 schema(全部表) */
private persistSchema;
/**
* 整表 diff 持久化:内存行全部 put + KV 残留行删除(原子 putMany + deleteMany)。
* 用于主键变更 / 级联 / dropTable / clear / alterTable DROP / 事务 commit。
*/
private flushTable;
}
declare class AriaEngine implements IStorageEngine {
@@ -1322,50 +1284,6 @@ interface Token {
/** 将 SQL 字符串解析为 Token 列表 */
declare function tokenize(sql: string): Token[];
/**
* AriaEngine Storage Backend — 存储后端抽象层
* @module engine/aria/store/backend
*
* 封装底层浏览器存储 APIIndexedDB / OPFS / Memory 回退),
* 供 Buffer Pool 的 PageIO 和 WAL 的 WALStore 使用。
*/
interface IStorageBackend {
/** 打开存储 */
open(name: string): Promise<void>;
/** 关闭存储 */
close(): Promise<void>;
/** 是否已打开 */
isOpen(): boolean;
/** 读取数据块 */
read(key: string): Promise<ArrayBuffer | null>;
/** 写入数据块 */
write(key: string, data: ArrayBuffer): Promise<void>;
/**
* 追加写入(v0.4.5 WAL 分片用,可选):
* - OPFS 后端实现真追加(createWritable keepExistingData + seekO(chunk)
* - 未实现的后端由调用方回退 read+writeEncryptedBackend 包装时整体重写保正确性)
* 语义:在 key 现有内容末尾追加 data;key 不存在时等同 write。
*/
append?(key: string, data: ArrayBuffer): Promise<void>;
/**
* 批量原子写入(v0.4.2-fix):多个 key 在单个底层事务中提交,
* 中断时整体回滚,不留半写状态。WAL count 与记录同事务保证一致性。
*/
writeMany(entries: Record<string, ArrayBuffer>): Promise<void>;
/** 删除数据块 */
delete(key: string): Promise<void>;
/**
* 批量原子删除(v0.4.2-fix):多个 key 在单个底层事务中提交。
*/
deleteMany(keys: string[]): Promise<void>;
/** 列出所有 key */
listKeys(): Promise<string[]>;
/** 检查 key 是否存在 */
exists(key: string): Promise<boolean>;
/** 清空所有数据 */
clear(): Promise<void>;
}
/**
* AriaEngine OPFS Backend — 基于 Origin Private File System 的自研存储后端
* @module engine/aria/store/opfs_backend
@@ -1463,4 +1381,4 @@ declare global {
declare const MeSqlark: typeof MetonaSqlark;
export { AriaEngine, AriaEngineConfig, ColumnDef, DatabaseConfig, DeleteStatement, DiskEngine, FieldType, HybridEngine, IStorageEngine, IndexedDBEngine, InsertStatement, MeSqlark, MemoryEngine, MetonaSqlark, OPFSBackend, OPFSEngine, SelectStatement, Statement, StorageMode, Table, TableSchema, UpdateStatement, VERSION, api, create, api as default, parse, parseAll, tokenize };
export { AriaEngine, AriaEngineConfig, ColumnDef, DatabaseConfig, DeleteStatement, DiskEngine, FieldType, HybridEngine, IStorageEngine, InsertStatement, KVStoreEngine, MeSqlark, MemoryEngine, MetonaSqlark, OPFSBackend, SelectStatement, Statement, StorageMode, Table, TableSchema, UpdateStatement, VERSION, api, create, api as default, parse, parseAll, tokenize };