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
+177 -185
View File
@@ -1,185 +1,177 @@
/**
* 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;
}
}
/**
* 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>;
/** v0.4.5: 批量分配页面 ID(一次 meta 持久化) */
allocatePageIds?(count: number): 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;
}
/**
* v0.4.5: 批量创建新页面(一次页面 ID 分配,页面化 SSTable 保存用)。
* 返回的页面均 pin 且 dirty=false(调用方写入后需 markDirty + flushPage)。
*/
async newPages(count: number, type: PageType = PageType.DATA): Promise<PageHandle[]> {
if (count <= 0) return [];
let pageIds: number[];
if (typeof this.pageIO.allocatePageIds === 'function') {
pageIds = await this.pageIO.allocatePageIds!(count);
} else {
pageIds = [];
for (let i = 0; i < count; i++) pageIds.push(await this.pageIO.allocatePageId());
}
await this.eviction.evictIfNeeded(count);
const handles: PageHandle[] = [];
for (const pageId of pageIds) {
const page = createPage(pageId, type);
page.pins = 1;
this.pages.set(pageId, page);
this.eviction.add(page);
handles.push(page);
}
return handles;
}
/**
* 释放页面的 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();
}
}