- 新增 AriaEngine: LSM-Tree 页面式存储引擎,19 个模块,~3500 行 TS - page/: Slotted Page 格式 (header/slot/tuple/format) + CRC32 - buffer/: Buffer Pool (LRU 缓存 + 驱逐策略) - index/: LSM-Tree (MemTable 红黑树 + SSTable + Bloom Filter + Merge Iterator) - wal/: WAL 日志 (二进制格式) + Checkpoint 管理 - transaction/: MVCC 版本链 + 快照隔离 - store/: IndexedDB / Memory 双后端抽象 - compression/: LZ4 页面压缩 - 完整持久化: Schema 自动保存、SSTable 元数据管理、WAL 恢复 - 事务感知 CRUD: insert/update/delete 在事务中缓冲到 snapshot - mode: 'aria' 激活自研引擎 - 新增 7 个测试文件,测试数 318 → 524,套件 20 → 27 - aria-page.test.ts (32 tests): Page 格式单元测试 - aria-index.test.ts (26 tests): Bloom Filter + MemTable - aria-sstable.test.ts (9 tests): SSTable Builder + Reader - aria-buffer.test.ts (25 tests): LRU + Eviction + Buffer Pool - aria-wal-mvcc.test.ts (22 tests): WAL 编解码 + MVCC 事务 - aria-compress.test.ts (11 tests): LZ4 + Merge Iterator - aria.test.ts (80 tests): AriaEngine 集成 + 边界测试 - Bug 修复: LRUList size 跟踪、WAL 缓冲区越界、ColumnEncoding 导入 - 全面更新 README.md + site/ 站点文件 (index/docs/demo)
180 lines
5.4 KiB
TypeScript
180 lines
5.4 KiB
TypeScript
/**
|
||
* AriaEngine Storage Backend — 存储后端抽象层
|
||
* @module engine/aria/store/backend
|
||
*
|
||
* 封装底层浏览器存储 API(IndexedDB / OPFS / Memory 回退),
|
||
* 供 Buffer Pool 的 PageIO 和 WAL 的 WALStore 使用。
|
||
*/
|
||
|
||
import { DatabaseError } from '../../../constants';
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// StorageBackend 接口
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export 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>;
|
||
/** 删除数据块 */
|
||
delete(key: string): Promise<void>;
|
||
/** 列出所有 key */
|
||
listKeys(): Promise<string[]>;
|
||
/** 检查 key 是否存在 */
|
||
exists(key: string): Promise<boolean>;
|
||
/** 清空所有数据 */
|
||
clear(): Promise<void>;
|
||
}
|
||
|
||
// =======================================================================
|
||
// IndexedDB Backend
|
||
// =======================================================================
|
||
|
||
export class IndexedDBBackend implements IStorageBackend {
|
||
private db: IDBDatabase | null = null;
|
||
private dbName = '';
|
||
private storeName = 'data';
|
||
|
||
async open(name: string): Promise<void> {
|
||
this.dbName = `aria-${name}`;
|
||
return new Promise((resolve, reject) => {
|
||
const request = indexedDB.open(this.dbName, 1);
|
||
request.onupgradeneeded = () => {
|
||
const db = request.result;
|
||
if (!db.objectStoreNames.contains(this.storeName)) {
|
||
db.createObjectStore(this.storeName);
|
||
}
|
||
};
|
||
request.onsuccess = () => {
|
||
this.db = request.result;
|
||
resolve();
|
||
};
|
||
request.onerror = () => reject(new DatabaseError('Failed to open AriaEngine IndexedDB', 'ARIA_IDB_OPEN_ERROR'));
|
||
});
|
||
}
|
||
|
||
async close(): Promise<void> {
|
||
if (this.db) {
|
||
this.db.close();
|
||
this.db = null;
|
||
}
|
||
}
|
||
|
||
isOpen(): boolean {
|
||
return this.db !== null;
|
||
}
|
||
|
||
async read(key: string): Promise<ArrayBuffer | null> {
|
||
const db = this.ensureDB();
|
||
return new Promise((resolve, reject) => {
|
||
const tx = db.transaction(this.storeName, 'readonly');
|
||
const req = tx.objectStore(this.storeName).get(key);
|
||
req.onsuccess = () => resolve(req.result ?? null);
|
||
req.onerror = () => reject(new DatabaseError('Failed to read from AriaEngine store', 'ARIA_READ_ERROR'));
|
||
});
|
||
}
|
||
|
||
async write(key: string, data: ArrayBuffer): Promise<void> {
|
||
const db = this.ensureDB();
|
||
return new Promise((resolve, reject) => {
|
||
const tx = db.transaction(this.storeName, 'readwrite');
|
||
tx.objectStore(this.storeName).put(data, key);
|
||
tx.oncomplete = () => resolve();
|
||
tx.onerror = () => reject(new DatabaseError('Failed to write to AriaEngine store', 'ARIA_WRITE_ERROR'));
|
||
});
|
||
}
|
||
|
||
async delete(key: string): Promise<void> {
|
||
const db = this.ensureDB();
|
||
return new Promise((resolve, reject) => {
|
||
const tx = db.transaction(this.storeName, 'readwrite');
|
||
tx.objectStore(this.storeName).delete(key);
|
||
tx.oncomplete = () => resolve();
|
||
tx.onerror = () => reject(new DatabaseError('Failed to delete from AriaEngine store', 'ARIA_DELETE_ERROR'));
|
||
});
|
||
}
|
||
|
||
async listKeys(): Promise<string[]> {
|
||
const db = this.ensureDB();
|
||
return new Promise((resolve, reject) => {
|
||
const tx = db.transaction(this.storeName, 'readonly');
|
||
const req = tx.objectStore(this.storeName).getAllKeys();
|
||
req.onsuccess = () => resolve((req.result ?? []) as string[]);
|
||
req.onerror = () => reject(new DatabaseError('Failed to list keys', 'ARIA_LIST_ERROR'));
|
||
});
|
||
}
|
||
|
||
async exists(key: string): Promise<boolean> {
|
||
const result = await this.read(key);
|
||
return result !== null;
|
||
}
|
||
|
||
async clear(): Promise<void> {
|
||
const db = this.ensureDB();
|
||
return new Promise((resolve, reject) => {
|
||
const tx = db.transaction(this.storeName, 'readwrite');
|
||
tx.objectStore(this.storeName).clear();
|
||
tx.oncomplete = () => resolve();
|
||
tx.onerror = () => reject(new DatabaseError('Failed to clear AriaEngine store', 'ARIA_CLEAR_ERROR'));
|
||
});
|
||
}
|
||
|
||
private ensureDB(): IDBDatabase {
|
||
if (!this.db) throw new DatabaseError('AriaEngine storage not opened', 'ARIA_DB_NOT_OPEN');
|
||
return this.db;
|
||
}
|
||
}
|
||
|
||
// =======================================================================
|
||
// Memory Backend(回退 / 测试用)
|
||
// =======================================================================
|
||
|
||
export class MemoryBackend implements IStorageBackend {
|
||
private store: Map<string, ArrayBuffer> = new Map();
|
||
private opened = false;
|
||
|
||
async open(_name: string): Promise<void> {
|
||
this.opened = true;
|
||
}
|
||
|
||
async close(): Promise<void> {
|
||
this.store.clear();
|
||
this.opened = false;
|
||
}
|
||
|
||
isOpen(): boolean {
|
||
return this.opened;
|
||
}
|
||
|
||
async read(key: string): Promise<ArrayBuffer | null> {
|
||
return this.store.get(key) ?? null;
|
||
}
|
||
|
||
async write(key: string, data: ArrayBuffer): Promise<void> {
|
||
this.store.set(key, data);
|
||
}
|
||
|
||
async delete(key: string): Promise<void> {
|
||
this.store.delete(key);
|
||
}
|
||
|
||
async listKeys(): Promise<string[]> {
|
||
return Array.from(this.store.keys());
|
||
}
|
||
|
||
async exists(key: string): Promise<boolean> {
|
||
return this.store.has(key);
|
||
}
|
||
|
||
async clear(): Promise<void> {
|
||
this.store.clear();
|
||
}
|
||
}
|