164 lines
5.0 KiB
TypeScript
164 lines
5.0 KiB
TypeScript
/**
|
||
* metona-sqlark Hybrid Engine — 内存 + 磁盘混合存储引擎
|
||
* @module hybrid/index
|
||
*
|
||
* 采用 write-through 策略:
|
||
* - 所有写操作同时写入内存和磁盘
|
||
* - 所有读操作直接从内存返回
|
||
* - 数据库打开时从磁盘加载数据到内存
|
||
*/
|
||
|
||
import type { IStorageEngine } from '../engine/interface';
|
||
import type { QueryPlan, TableSchema, DiskEngine } from '../constants';
|
||
import { MemoryEngine } from '../engine/memory';
|
||
import { IndexedDBEngine } from '../engine/indexeddb';
|
||
import { OPFSEngine } from '../engine/opfs';
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// HybridEngine
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export class HybridEngine implements IStorageEngine {
|
||
readonly name = 'hybrid';
|
||
|
||
private memoryEngine: MemoryEngine;
|
||
private diskEngine: IStorageEngine;
|
||
private diskEngineType: DiskEngine;
|
||
|
||
constructor(diskEngine: DiskEngine = 'indexeddb') {
|
||
this.memoryEngine = new MemoryEngine();
|
||
this.diskEngineType = diskEngine;
|
||
this.diskEngine = diskEngine === 'opfs' ? new OPFSEngine() : new IndexedDBEngine();
|
||
}
|
||
|
||
// ---- 生命周期 ----
|
||
|
||
async open(dbName: string, version: number): Promise<void> {
|
||
// 先打开磁盘引擎
|
||
await this.diskEngine.open(dbName, version);
|
||
|
||
// 再打开内存引擎
|
||
await this.memoryEngine.open(dbName, version);
|
||
|
||
// 从磁盘加载现存表
|
||
const tableNames = await this.diskEngine.getTableNames();
|
||
for (const tableName of tableNames) {
|
||
const schema = await this.diskEngine.getTableSchema(tableName);
|
||
if (!schema) continue;
|
||
|
||
// 在内存中创建表
|
||
await this.memoryEngine.createTable(schema);
|
||
|
||
// 从磁盘加载数据到内存
|
||
const rows = await this.diskEngine.find(tableName, { table: tableName });
|
||
if (rows.length > 0) {
|
||
try {
|
||
await this.memoryEngine.insert(tableName, rows);
|
||
} catch (e) {
|
||
// eslint-disable-next-line no-console
|
||
console.warn(`[metona-sqlark] Failed to load table "${tableName}" data from disk:`, e);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
async close(): Promise<void> {
|
||
await this.memoryEngine.close();
|
||
await this.diskEngine.close();
|
||
}
|
||
|
||
isOpen(): boolean {
|
||
return this.memoryEngine.isOpen() && this.diskEngine.isOpen();
|
||
}
|
||
|
||
// ---- 表管理 ----
|
||
|
||
async createTable(schema: TableSchema): Promise<void> {
|
||
await this.memoryEngine.createTable(schema);
|
||
await this.diskEngine.createTable(schema);
|
||
}
|
||
|
||
async dropTable(tableName: string): Promise<void> {
|
||
await this.memoryEngine.dropTable(tableName);
|
||
await this.diskEngine.dropTable(tableName);
|
||
}
|
||
|
||
async hasTable(tableName: string): Promise<boolean> {
|
||
return this.memoryEngine.hasTable(tableName);
|
||
}
|
||
|
||
async getTableNames(): Promise<string[]> {
|
||
return this.memoryEngine.getTableNames();
|
||
}
|
||
|
||
async getTableSchema(tableName: string): Promise<TableSchema | null> {
|
||
return this.memoryEngine.getTableSchema(tableName);
|
||
}
|
||
|
||
// ---- CRUD(write-through 策略) ----
|
||
|
||
async insert(tableName: string, rows: Record<string, unknown>[]): Promise<string[]> {
|
||
const pks = await this.memoryEngine.insert(tableName, rows);
|
||
// write-through: 同步写入磁盘
|
||
await this.diskEngine.insert(tableName, rows);
|
||
return pks;
|
||
}
|
||
|
||
async find(tableName: string, query: QueryPlan): Promise<Record<string, unknown>[]> {
|
||
// 直接从内存读取
|
||
return this.memoryEngine.find(tableName, query);
|
||
}
|
||
|
||
async update(tableName: string, query: QueryPlan, updates: Record<string, unknown>): Promise<number> {
|
||
const count = await this.memoryEngine.update(tableName, query, updates);
|
||
// write-through: 同步更新磁盘
|
||
await this.diskEngine.update(tableName, query, updates);
|
||
return count;
|
||
}
|
||
|
||
async delete(tableName: string, query: QueryPlan): Promise<number> {
|
||
const count = await this.memoryEngine.delete(tableName, query);
|
||
// write-through: 同步删除磁盘
|
||
await this.diskEngine.delete(tableName, query);
|
||
return count;
|
||
}
|
||
|
||
async count(tableName: string, query?: QueryPlan): Promise<number> {
|
||
return this.memoryEngine.count(tableName, query);
|
||
}
|
||
|
||
async clear(tableName: string): Promise<void> {
|
||
await this.memoryEngine.clear(tableName);
|
||
await this.diskEngine.clear(tableName);
|
||
}
|
||
|
||
// ---- 事务 ----
|
||
|
||
async beginTransaction(): Promise<void> {
|
||
await this.memoryEngine.beginTransaction();
|
||
await this.diskEngine.beginTransaction();
|
||
}
|
||
|
||
async commitTransaction(): Promise<void> {
|
||
await this.memoryEngine.commitTransaction();
|
||
await this.diskEngine.commitTransaction();
|
||
}
|
||
|
||
async rollbackTransaction(): Promise<void> {
|
||
await this.memoryEngine.rollbackTransaction();
|
||
await this.diskEngine.rollbackTransaction();
|
||
}
|
||
|
||
// ---- 引擎信息 ----
|
||
|
||
/** 获取磁盘引擎类型 */
|
||
getDiskEngineType(): DiskEngine {
|
||
return this.diskEngineType;
|
||
}
|
||
|
||
/** 获取内存引擎(供内部使用) */
|
||
getMemoryEngine(): MemoryEngine {
|
||
return this.memoryEngine;
|
||
}
|
||
}
|