fix: v0.6.0 复查修正 — KVStore 快照损坏水位 bug(metaSeq 误跳日志)+ 快照损坏测试盲区修复(此前篡改未生效)+ metaSeq 回归测试 + KVStoreEngine 真实浏览器 e2e(3 用例)+ 版本/配置文档校准
CI / test (20.x) (push) Successful in 10m51s
CI / test (18.x) (push) Successful in 10m59s
CI / test (22.x) (push) Successful in 10m47s
CI / test (24.x) (push) Successful in 10m41s
CI / e2e (push) Successful in 9m52s

This commit is contained in:
thzxx
2026-08-10 14:11:44 +08:00
parent 24b3ca1079
commit a9e1a281d3
20 changed files with 186 additions and 140 deletions
+8 -23
View File
@@ -980,10 +980,6 @@ class SharedMemoryBackend {
static clearRegistry() {
registry.clear();
}
/** 注册表中的库数量(测试诊断用) */
static registrySize() {
return registry.size;
}
async open(name) {
this.dbName = name;
if (!registry.has(name)) {
@@ -1396,10 +1392,6 @@ class KVStore {
this.medium = medium ?? defaultMedium();
this.checkpointThreshold = checkpointThreshold;
}
/** 底层介质(测试/诊断用) */
getMedium() {
return this.medium;
}
isOpen() {
return this.opened;
}
@@ -1415,17 +1407,8 @@ class KVStore {
this.index = new Map();
this.seq = 0;
this.logBytes = 0;
// 1. 读 meta(可能缺失/损坏
let metaSeq = 0;
const metaRaw = await this.medium.read(META_KEY);
if (metaRaw) {
try {
const meta = JSON.parse(new TextDecoder().decode(metaRaw));
metaSeq = Number(meta.seq) || 0;
}
catch { /* meta 损坏:回退全量日志 */ }
}
// 2. 加载快照(损坏则全量日志重放)
// 1. 加载快照(损坏则全量日志重放;快照内嵌 seq 为日志水位权威
// metacheckpoint 写入)仅标记库存在,不参与水位判断
let snapshotSeq = 0;
const snapshotRaw = await this.medium.read(SNAPSHOT_KEY);
if (snapshotRaw) {
@@ -1443,10 +1426,15 @@ class KVStore {
}
}
// 3. 重放日志(seq > 快照水位的记录)
// v0.6.1-fix: 快照水位只信任快照内嵌 seqsnapshotSeq)。
// 此前 baseSeq = max(metaSeq, snapshotSeq):快照损坏回退全量重放时,
// meta.seq(最后一次 checkpoint 水位)会错误跳过日志中 checkpoint 后
// 的有效记录(日志被截断过,metaSeq 不代表日志内容水位)→ 丢数据。
// meta 仅在库存在性标记,不参与水位判断。
const logRaw = await this.medium.read(LOG_KEY);
if (logRaw && logRaw.byteLength > 0) {
const log = new Uint8Array(logRaw);
const baseSeq = Math.max(metaSeq, snapshotSeq);
const baseSeq = snapshotSeq;
const corruptOffsets = [];
const applied = parseLogRecords(log, (record) => {
if (record.seq <= baseSeq)
@@ -1517,9 +1505,6 @@ class KVStore {
size() {
return this.index.size;
}
getSeq() {
return this.seq;
}
// =======================================================================
// 写入(原子事务)
// =======================================================================