release: v0.6.0 — 完全移除 IndexedDB,自研 KVStore 事务存储引擎(多key原子写/快照日志恢复/CRC自愈)+ KVStoreEngine + 旧库迁移工具 + 10万级压力验证 + 崩溃注入e2e
This commit is contained in:
@@ -119,6 +119,74 @@ test.describe('OPFS 真实环境', () => {
|
||||
await page2.close();
|
||||
});
|
||||
|
||||
test('写入进行中崩溃(不等待完成)→ 重开数据不损坏且可恢复已确认写入', async ({ page }) => {
|
||||
await openPage(page);
|
||||
await run(page, 'open', {
|
||||
name: 'e2e-crash-mid', diskEngine: 'opfs', checkpointInterval: 999999999,
|
||||
});
|
||||
await run(page, 'createTable', {
|
||||
table: 't', columns: [{ name: 'id', type: 'string', primaryKey: true }],
|
||||
});
|
||||
// 前 20 条确认完成
|
||||
for (let i = 0; i < 20; i++) {
|
||||
await run(page, 'insert', { table: 't', rows: [{ id: `c-${i}` }] });
|
||||
}
|
||||
// 发起一批写入后立即崩溃(不等 Promise 完成)
|
||||
const pending = page.evaluate(async () => {
|
||||
const ms = (window as unknown as { __ms: { insert: (o: unknown) => Promise<unknown> } }).__ms;
|
||||
const rows = [];
|
||||
for (let i = 0; i < 30; i++) rows.push({ id: `p-${i}` });
|
||||
await ms.insert({ table: 't', rows });
|
||||
});
|
||||
await new Promise((r) => setTimeout(r, 50));
|
||||
await crashPage(page);
|
||||
await pending.catch(() => {});
|
||||
|
||||
// 重开:库可打开,已确认 20 条完整;写入中数据要么全部可见要么部分(日志尾部截断)
|
||||
const page2 = await page.context().newPage();
|
||||
await openPage(page2);
|
||||
await run(page2, 'open', {
|
||||
name: 'e2e-crash-mid', diskEngine: 'opfs', checkpointInterval: 999999999,
|
||||
});
|
||||
const res = await run<{ count: number }>(page2, 'count', { table: 't' });
|
||||
expect(res.count).toBeGreaterThanOrEqual(20);
|
||||
// 已确认的 20 条必须完整
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const found = await run<{ rows: Record<string, unknown>[] }>(page2, 'find', {
|
||||
table: 't', where: { id: `c-${i}` },
|
||||
});
|
||||
expect(found.rows).toHaveLength(1);
|
||||
}
|
||||
await run(page2, 'close');
|
||||
await page2.close();
|
||||
});
|
||||
|
||||
test('checkpoint 前后崩溃 → 快照/日志双路径恢复一致', async ({ page }) => {
|
||||
await openPage(page);
|
||||
await run(page, 'open', { name: 'e2e-crash-cp', diskEngine: 'opfs', checkpointInterval: 999999999 });
|
||||
await run(page, 'createTable', {
|
||||
table: 't', columns: [{ name: 'id', type: 'string', primaryKey: true }],
|
||||
});
|
||||
for (let i = 0; i < 10; i++) {
|
||||
await run(page, 'insert', { table: 't', rows: [{ id: `a-${i}` }] });
|
||||
}
|
||||
// 触发 checkpoint(强制落盘)
|
||||
await run(page, 'repair'); // repair 内含 flush+checkpoint
|
||||
for (let i = 0; i < 10; i++) {
|
||||
await run(page, 'insert', { table: 't', rows: [{ id: `b-${i}` }] });
|
||||
}
|
||||
// 崩溃(checkpoint 后数据在快照,后续在日志)
|
||||
await crashPage(page);
|
||||
|
||||
const page2 = await page.context().newPage();
|
||||
await openPage(page2);
|
||||
await run(page2, 'open', { name: 'e2e-crash-cp', diskEngine: 'opfs', checkpointInterval: 999999999 });
|
||||
const res = await run<{ count: number }>(page2, 'count', { table: 't' });
|
||||
expect(res.count).toBe(20);
|
||||
await run(page2, 'close');
|
||||
await page2.close();
|
||||
});
|
||||
|
||||
test('多标签页锁:第二个标签页打开同一库 → ARIA_LOCKED', async ({ page }) => {
|
||||
await openPage(page);
|
||||
await run(page, 'open', { name: 'e2e-lock', diskEngine: 'opfs' });
|
||||
|
||||
Reference in New Issue
Block a user