build: 重建 dist(B-6 manifest 单一提交点 + LSM 结构根治 + 新错误码)
This commit is contained in:
Vendored
+2396
-675
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+151
-4
@@ -1639,6 +1639,24 @@ declare class KVStoreEngine implements IStorageEngine {
|
|||||||
private collectTableDiff;
|
private collectTableDiff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** v0.8.0:引擎恢复诊断(供上层展示/断言;不静默) */
|
||||||
|
interface AriaRecoveryReport {
|
||||||
|
/** 打开过程中被丢弃的 SSTable(按命名空间) */
|
||||||
|
droppedSSTables: {
|
||||||
|
namespace: string;
|
||||||
|
id: number;
|
||||||
|
level: number;
|
||||||
|
reason: string;
|
||||||
|
}[];
|
||||||
|
/** 是否怀疑已确认写入丢失(被丢弃的 SSTable 没有 WAL 兜底) */
|
||||||
|
dataLossSuspected: boolean;
|
||||||
|
/** WAL 活跃区间内的分片空洞(已提交事务记录缺失) */
|
||||||
|
walGaps: number[];
|
||||||
|
/** 本次打开是否从旧格式(__aria_lsm_meta/__aria_schemas)迁移而来 */
|
||||||
|
legacyImported: boolean;
|
||||||
|
/** 是否从更早的 manifest 世代回退(最新世代损坏) */
|
||||||
|
manifestFallback: boolean;
|
||||||
|
}
|
||||||
declare class AriaEngine implements IStorageEngine {
|
declare class AriaEngine implements IStorageEngine {
|
||||||
readonly name = "aria";
|
readonly name = "aria";
|
||||||
/**
|
/**
|
||||||
@@ -1655,6 +1673,31 @@ declare class AriaEngine implements IStorageEngine {
|
|||||||
private fileManager;
|
private fileManager;
|
||||||
private bufferPool;
|
private bufferPool;
|
||||||
private dbLock;
|
private dbLock;
|
||||||
|
/**
|
||||||
|
* v0.8.0(B-6):存储层单一提交点。
|
||||||
|
*
|
||||||
|
* 页面水位 / 各命名空间 SSTable 列表 / 表结构 / WAL 起始位置 / 待落盘冻结表意图
|
||||||
|
* 全部收敛到 `__aria_manifest_<generation>`:数据先落盘,再提交 manifest,
|
||||||
|
* **提交成功后**才允许截断 WAL 或删除旧分片。恢复只认最后一份 CRC 通过的世代。
|
||||||
|
*/
|
||||||
|
private manifestStore;
|
||||||
|
private manifest;
|
||||||
|
/**
|
||||||
|
* v0.8.0:已确认落盘的 WAL 水位(LSN)。
|
||||||
|
*
|
||||||
|
* 只在"所有 LSM 都没有未落盘数据"时推进到当前 LSN —— 于是
|
||||||
|
* `lsn <= durableLsn` 的记录必然已存在于已提交的 SSTable 中,
|
||||||
|
* 恢复时可以安全跳过(也就允许删除对应分片)。
|
||||||
|
*/
|
||||||
|
private durableLsn;
|
||||||
|
/**
|
||||||
|
* v0.8.0:仍需保留的最小 WAL 分片号(每次 `checkpointBefore` 的返回值)。
|
||||||
|
* 写进 manifest 的 `wal.startSegment`:即便分片删除只完成一半,恢复也只从
|
||||||
|
* 这个分片开始读,不会把上一世代的旧记录排到新记录之后重放。
|
||||||
|
*/
|
||||||
|
private walStartSegment;
|
||||||
|
/** v0.8.0:恢复诊断 */
|
||||||
|
private recoveryReport;
|
||||||
private schemas;
|
private schemas;
|
||||||
private tablePKs;
|
private tablePKs;
|
||||||
private opCounter;
|
private opCounter;
|
||||||
@@ -1679,13 +1722,25 @@ declare class AriaEngine implements IStorageEngine {
|
|||||||
* v0.4.2-fix: 崩溃恢复/自愈 — 校验并移除损坏 SSTable、截断 WAL、重建二级索引。
|
* v0.4.2-fix: 崩溃恢复/自愈 — 校验并移除损坏 SSTable、截断 WAL、重建二级索引。
|
||||||
* v0.4.5 增强:清理 OPFS 残留临时文件、清理孤儿页面(meta 未引用的 pg_ 文件)。
|
* v0.4.5 增强:清理 OPFS 残留临时文件、清理孤儿页面(meta 未引用的 pg_ 文件)。
|
||||||
* 应用层检测到异常后调用,无需删库重建。
|
* 应用层检测到异常后调用,无需删库重建。
|
||||||
|
*
|
||||||
|
* v0.8.0(B-6):孤儿回收**必须**以"manifest 健康"为前提。
|
||||||
|
* 修复前 `cleanupOrphanPages` 只读裸 JSON meta,读不出来就当"没有任何引用",
|
||||||
|
* 于是元数据损坏时 repair 会把全部活页删掉(不可逆)。现在:
|
||||||
|
* - 只要本次打开出现过损坏世代/被丢弃的 SSTable → 直接跳过回收并告警;
|
||||||
|
* - 引用集合来自 manifest 的权威 meta 列表。
|
||||||
*/
|
*/
|
||||||
repair(): Promise<void>;
|
repair(): Promise<void>;
|
||||||
/**
|
/**
|
||||||
* v0.4.5: 清理孤儿页面 — 扫描全部 pg_* 文件,未被任何 LSM 命名空间 meta 引用的删除。
|
* v0.4.5: 清理孤儿页面 — 扫描全部 pg_* 文件,未被任何命名空间 meta 引用的删除。
|
||||||
* 孤儿页面来自:崩溃中断的 compaction/删除流程(旧 SSTable 页面残留)。
|
* 孤儿页面来自:崩溃中断的 compaction/删除流程(旧 SSTable 页面残留)。
|
||||||
|
*
|
||||||
|
* v0.8.0(B-6):引用集合取自 manifest;且**只有在没有损坏迹象时才执行** ——
|
||||||
|
* "任何引用不到的东西一律保留而非删除"在恢复路径上是不变量,只有显式 repair
|
||||||
|
* 且 manifest 完整可信时才允许回收空间。
|
||||||
*/
|
*/
|
||||||
private cleanupOrphanPages;
|
private cleanupOrphanPages;
|
||||||
|
/** v0.8.0: 读取某个 LSM 当前引用的层结构(诊断/孤儿回收用) */
|
||||||
|
private getLsmLevels;
|
||||||
/**
|
/**
|
||||||
* v0.4.1: 重置数据库 — 清空全部数据与表结构(演示页刷新/重新初始化用)。
|
* v0.4.1: 重置数据库 — 清空全部数据与表结构(演示页刷新/重新初始化用)。
|
||||||
* 清空存储后端、LSM、WAL、MVCC 与二级索引,后续可继续使用本实例。
|
* 清空存储后端、LSM、WAL、MVCC 与二级索引,后续可继续使用本实例。
|
||||||
@@ -1797,15 +1852,107 @@ declare class AriaEngine implements IStorageEngine {
|
|||||||
* v0.8.0(B-1):写入前置校验 —— 见 `IStorageEngine.validatePayload` 契约。
|
* v0.8.0(B-1):写入前置校验 —— 见 `IStorageEngine.validatePayload` 契约。
|
||||||
*/
|
*/
|
||||||
validatePayload(tableName: string, rows: Record<string, unknown>[], mode?: 'insert' | 'update'): Promise<void>;
|
validatePayload(tableName: string, rows: Record<string, unknown>[], mode?: 'insert' | 'update'): Promise<void>;
|
||||||
|
/**
|
||||||
|
* v0.8.0(B-6):表结构**随 manifest 一起提交**(单一提交点)。
|
||||||
|
*
|
||||||
|
* 修复前 DDL 结束时会单独写一份 `__aria_schemas`:结构变更与存储状态
|
||||||
|
* (SSTable meta / 页面 / WAL 水位)各自落盘,中间崩溃就会留下"结构说加过列、
|
||||||
|
* 数据里没有"或反之的分裂状态。现在两者在同一次原子提交里生效。
|
||||||
|
*/
|
||||||
private persistSchemas;
|
private persistSchemas;
|
||||||
private loadSchemas;
|
private loadSchemas;
|
||||||
/**
|
/**
|
||||||
* 创建命名空间隔离的 SSTableStore。
|
* v0.8.0: 恢复诊断(打开时被丢弃的 SSTable、WAL 空洞、是否怀疑数据丢失)。
|
||||||
|
*
|
||||||
|
* 数据来自两处:引擎层(WAL 空洞 / 迁移 / manifest 回退)与各 LSM(被丢弃的
|
||||||
|
* SSTable + 其 `dataLossSuspected`),这里合并成一份对外的报告 —— 于是
|
||||||
|
* "这次打开到底自愈了什么、有没有真丢数据"是**可读的返回值**而不是只能翻日志。
|
||||||
|
*/
|
||||||
|
getRecoveryReport(): AriaRecoveryReport;
|
||||||
|
/**
|
||||||
|
* v0.8.0: LSM 的唯一构造点。
|
||||||
|
*
|
||||||
|
* 为什么集中:本项目最反复的缺陷模式就是"同一语义在多处实现、只修一处"
|
||||||
|
* (审计结论的原话)。主 LSM 与二级索引 LSM 的配置必须完全一致地带上
|
||||||
|
* 命名空间、WAL LSN 提供者与 durable-coverage 语义,因此只能有一个工厂。
|
||||||
|
*/
|
||||||
|
private createLSM;
|
||||||
|
/** 全部 LSM(主 + 二级索引) */
|
||||||
|
private allLsms;
|
||||||
|
/**
|
||||||
|
* v0.8.0: 把全部 LSM 的数据落盘。
|
||||||
|
* @param memtablesOnly true = 只落 memtable,不等 compaction(checkpoint 用)
|
||||||
|
*/
|
||||||
|
private flushAllLsms;
|
||||||
|
/** v0.8.0: 是否存在任何未落盘的 LSM 数据(决定 WAL 水位能否推进) */
|
||||||
|
private hasPendingFlushData;
|
||||||
|
/** schema 的持久化形态(与旧 `__aria_schemas` 同形) */
|
||||||
|
private serializeSchemas;
|
||||||
|
/** 所有 LSM 的待落盘冻结表意图(manifest 记录,阻止 WAL 水位越过它们) */
|
||||||
|
private collectFrozenIntents;
|
||||||
|
/**
|
||||||
|
* v0.8.0(B-6):**唯一的 manifest 提交入口**。
|
||||||
|
*
|
||||||
|
* 每次提交都重新计算权威字段,因此并发/交错场景下"最后一次提交"总是包含
|
||||||
|
* 完整的最新状态:
|
||||||
|
* - `pageIdWatermark`:单调推进、永不复用;
|
||||||
|
* - `schemas`:表结构的权威描述(DDL 不再依赖独立的 `__aria_schemas` 提交);
|
||||||
|
* - `frozen`:待落盘冻结表意图;
|
||||||
|
* - `wal.startLsn`:**只有全部数据已落盘时才推进**(否则保持原值),
|
||||||
|
* 这是"截断 WAL 前必须先提交 manifest"的可验证形式。
|
||||||
|
*/
|
||||||
|
private commitManifest;
|
||||||
|
/**
|
||||||
|
* v0.8.0:计算"当前可以保证的落盘水位"(纯函数,不改状态)。
|
||||||
|
*
|
||||||
|
* 三条规则:
|
||||||
|
* - 有冻结表意图 → 水位不得越过最早的冻结表内容起点(它们的记录只在内存+WAL);
|
||||||
|
* - 有未落盘 memtable → 维持原水位;
|
||||||
|
* - 全部落盘 → 推进到当前 LSN(这些记录已存在于已提交的 SSTable 中)。
|
||||||
|
*/
|
||||||
|
private computeDurableLsn;
|
||||||
|
/**
|
||||||
|
* v0.8.0(B-6):WAL 检查点的**唯一实现** —— "先算边界 → 提交 manifest → 再删除"。
|
||||||
|
*
|
||||||
|
* 顺序不可交换(见 `SegmentedWALStore.planKeepFrom` 的说明)。所有需要回收 WAL
|
||||||
|
* 空间的路径(打开恢复后、close、repair、周期 checkpoint)都必须走这里,
|
||||||
|
* 否则又会出现"同一语义多处实现、只改一处"的老问题。
|
||||||
|
*/
|
||||||
|
private advanceWalCheckpoint;
|
||||||
|
/**
|
||||||
|
* v0.8.0:旧格式(v0.8.0 之前)状态导入。
|
||||||
|
*
|
||||||
|
* 导入必须是**全有或全无**的:
|
||||||
|
* - 旧 meta 存在但无法解析 → 抛错(`ARIA_LEGACY_META_CORRUPT`)。
|
||||||
|
* 修复前 `readMetaList()` 遇到坏 JSON 返回 `[]`,于是"元数据损坏"直接
|
||||||
|
* 表现为"空库",随后 repair 还会把没人引用的活页全部删掉(不可逆)。
|
||||||
|
*/
|
||||||
|
private importLegacyState;
|
||||||
|
/**
|
||||||
|
* v0.8.0:恢复后校验冻结表意图。
|
||||||
|
*
|
||||||
|
* 语义:manifest 记录了"某张冻结表还没落盘"(意图),说明它的数据要么在 WAL 里,
|
||||||
|
* 要么已经在 SSTable 里。若本次打开**一条 WAL 记录都没有重放到**,而 manifest
|
||||||
|
* 又声称有未落盘数据,那么这些"已确认写入"就是真的丢了(WAL 被截断/介质丢失)。
|
||||||
|
* 此时抛错 —— 修复前这种丢失完全不可观测。
|
||||||
|
*/
|
||||||
|
private verifyFrozenIntentsAfterRecovery;
|
||||||
|
/**
|
||||||
|
* 创建命名空间隔离的 SSTableStore(**manifest 权威**)。
|
||||||
*
|
*
|
||||||
* 主 LSM 与每个二级索引 LSM 各持有独立实例:
|
* 主 LSM 与每个二级索引 LSM 各持有独立实例:
|
||||||
* - 文件 key 前缀隔离(sst_ / sst_idx_${table}_${col}_)
|
* - 文件 key 前缀隔离(sst_ / sst_idx_${table}_${col}_)
|
||||||
* - 元数据 key 隔离(__aria_lsm_meta / __aria_lsm_meta_${ns})
|
* - 元数据在 manifest 的 `namespaces[ns]` 中隔离(不再各写一份裸 JSON)
|
||||||
* - id 序列独立(避免 v0.2.4 共享 id 空间导致的文件互相覆盖)
|
* - id 序列独立且**单调推进**(`nextSstableId` 记在 manifest 里,
|
||||||
|
* 即便某一代 SSTable 全部被删除,id 也不会被复用)
|
||||||
|
*
|
||||||
|
* v0.8.0(B-6)两处结构变化:
|
||||||
|
* 1. **meta 不再走裸 JSON**:`saveMeta`/`deleteMeta` 直接改 manifest 并提交。
|
||||||
|
* 修复前 `JSON.parse` 失败 → 返回 `[]` → 元数据损坏 = 静默空库
|
||||||
|
* (随后 repair 还会把没人引用的活页删掉);
|
||||||
|
* 2. `load/delete` 的页面映射改由 PageSSTableStore 自己维护
|
||||||
|
* (活跃 + 退休两张表)—— 于是"compaction 摘除 meta"与
|
||||||
|
* "在途读者按 id 读取"不再互相矛盾。
|
||||||
*/
|
*/
|
||||||
private createSSTableStore;
|
private createSSTableStore;
|
||||||
/** v0.4.5: 是否启用页面化物理存储(默认 OPFS / KVStore 后端启用,显式配置可覆盖) */
|
/** v0.4.5: 是否启用页面化物理存储(默认 OPFS / KVStore 后端启用,显式配置可覆盖) */
|
||||||
|
|||||||
Vendored
+2396
-675
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+2396
-675
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+2396
-675
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+2396
-675
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user