feat: v0.2.0 AriaEngine 自研存储引擎
CI / test (20.x) (push) Canceled after 0s
CI / test (22.x) (push) Canceled after 0s
CI / test (24.x) (push) Canceled after 0s
CI / test (18.x) (push) Canceled after 1h26m17s

- 新增 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)
This commit is contained in:
2026-07-27 16:40:29 +08:00
parent c00738aea0
commit f84673e519
47 changed files with 14553 additions and 108 deletions
+185
View File
@@ -0,0 +1,185 @@
/**
* AriaEngine Buffer Pool — 页面缓存池
* @module engine/aria/buffer/pool
*
* 管理固定数量页面的 LRU 缓存,减少对底层储存的访问。
*/
import type { PageHandle } from '../types';
import { PageType, DEFAULT_BUFFER_POOL_PAGES } from '../types';
import { createPage } from '../page/format';
import { EvictionManager } from './eviction';
// ---------------------------------------------------------------------------
// Page Read / Write 回调
// ---------------------------------------------------------------------------
export interface PageIO {
/** 从存储后端读取页面 */
readPage(pageId: number): Promise<ArrayBuffer | null>;
/** 将页面写入存储后端 */
writePage(pageId: number, data: ArrayBuffer): Promise<void>;
/** 分配新页面 ID */
allocatePageId(): Promise<number>;
/** 释放页面 ID */
freePageId(pageId: number): Promise<void>;
}
// ---------------------------------------------------------------------------
// Buffer Pool
// ---------------------------------------------------------------------------
export class BufferPool {
private pages: Map<number, PageHandle> = new Map();
private eviction: EvictionManager;
private pageIO: PageIO;
private nextPageId = 0;
constructor(pageIO: PageIO, capacity: number = DEFAULT_BUFFER_POOL_PAGES) {
this.pageIO = pageIO;
this.eviction = new EvictionManager(capacity, async (page) => {
if (page.dirty) {
await this.pageIO.writePage(page.pageId, page.data);
page.dirty = false;
}
});
}
// -----------------------------------------------------------------------
// 页面获取
// -----------------------------------------------------------------------
/**
* 获取页面(必要时从磁盘读取)。
* 返回 pin 的页面,使用完成后必须调用 unpin()。
*/
async getPage(pageId: number): Promise<PageHandle | null> {
// 已在池中
let page = this.pages.get(pageId);
if (page) {
this.eviction.access(page);
page.pins++;
return page;
}
// 需要从磁盘加载
const buffer = await this.pageIO.readPage(pageId);
if (!buffer) return null;
// 确保有空间
await this.eviction.evictIfNeeded(1);
const type = new DataView(buffer).getUint8(4) as PageType;
page = {
pageId,
type,
data: buffer,
dirty: false,
pins: 1,
prev: null,
next: null,
lastAccess: Date.now(),
};
this.pages.set(pageId, page);
this.eviction.add(page);
return page;
}
/**
* 创建新页面。
*/
async newPage(type: PageType = PageType.DATA): Promise<PageHandle> {
const pageId = await this.pageIO.allocatePageId();
await this.eviction.evictIfNeeded(1);
const page = createPage(pageId, type);
page.pins = 1;
this.pages.set(pageId, page);
this.eviction.add(page);
return page;
}
/**
* 释放页面的 pin。
*/
unpin(page: PageHandle): void {
if (page.pins > 0) {
page.pins--;
}
}
/**
* 标记页面为脏(需要写回)。
*/
markDirty(page: PageHandle): void {
page.dirty = true;
}
/**
* 将脏页面刷新到磁盘。
*/
async flushPage(pageId: number): Promise<void> {
const page = this.pages.get(pageId);
if (page && page.dirty) {
await this.pageIO.writePage(pageId, page.data);
page.dirty = false;
}
}
/**
* 刷新所有脏页面。
*/
async flushAll(): Promise<void> {
for (const [, page] of this.pages) {
if (page.dirty) {
await this.pageIO.writePage(page.pageId, page.data);
page.dirty = false;
}
}
}
/**
* 从缓存中删除指定页面(不刷盘)。
*/
removePage(pageId: number): void {
const page = this.pages.get(pageId);
if (page) {
this.eviction.remove(page);
this.pages.delete(pageId);
}
}
/**
* 清空缓存池(先刷脏页)。
*/
async clear(): Promise<void> {
await this.flushAll();
this.pages.clear();
this.eviction.clear();
}
// -----------------------------------------------------------------------
// 统计
// -----------------------------------------------------------------------
/** 获取当前缓存页面数 */
getCachedPageCount(): number {
return this.pages.size;
}
/** 获取缓存容量 */
getCapacity(): number {
return this.eviction.getCapacity();
}
/** 获取脏页面数 */
getDirtyPageCount(): number {
let count = 0;
for (const [, page] of this.pages) {
if (page.dirty) count++;
}
return count;
}
}