release: v0.3.2 — 质量加固 + SQL扩展 + 表达式 + 并发同步
v0.2.6 质量加固: - 修复 AriaEngine 二级索引 SSTable 互相覆盖(命名空间隔离) - 修复 LSM 多版本读取顺序错误 + MergeIterator 取最新来源 - 重写 LZ4 压缩器(往返一致性 + 缓冲区溢出) - sstableCache LRU 上限 + 预加载兜底(BufferPool 配置生效) - 修复 React/Vue 集成 import type 运行时 bug + exports 子路径 - 新增 38 个测试(LZ4往返/Crypto/集成), 删除伪测试 v0.3.0 SQL 功能扩展: - 多语句 parseAll + 事务语句 BEGIN/COMMIT/ROLLBACK - INSERT INTO ... SELECT + UNION/UNION ALL + EXISTS 关联子查询 - CREATE/DROP INDEX 五引擎实现 + 别名 WHERE 修复 - benchmark 页面 + 36 个新测试 v0.3.1 表达式与性能: - CASE WHEN 表达式(SELECT 列/WHERE/聚合) - JOIN + 关联子查询逐行绑定 - WAL 批量组提交(写放大 O(N)→O(1)) - 修复 pending frozen 可见性 + flush 缓存竞争 v0.3.2 并发: - CASE WHEN 用于 WHERE/聚合 + JOIN 哈希连接 - 多标签页同步(multiTabSync + BroadcastChannel) - IndexedDB schema 持久化(reopen 后表结构恢复) - 修复 where-matcher 顶层 $not - 修复 CJS 产物 .js 被 ESM 解析(exports 空) — .cjs 后缀 + exports 修正 - 836 测试 / 44 套件 / 81.0% 覆盖率
This commit is contained in:
+185
-185
@@ -1,185 +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;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user