Files
MetonaSqlark/tests/engine/kvstore-stress.test.ts
T
thzxx 24b3ca1079
CI / test (20.x) (push) Successful in 10m53s
CI / test (22.x) (push) Successful in 10m47s
CI / test (24.x) (push) Successful in 10m42s
CI / e2e (push) Successful in 10m27s
CI / test (18.x) (push) Successful in 11m1s
release: v0.6.0 — 完全移除 IndexedDB,自研 KVStore 事务存储引擎(多key原子写/快照日志恢复/CRC自愈)+ KVStoreEngine + 旧库迁移工具 + 10万级压力验证 + 崩溃注入e2e
2026-08-10 13:56:04 +08:00

140 lines
4.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* KVStoreEngine — 大规模压力测试(10 万级)
*
* 验证生产级可靠性:
* 1. 10 万 key 批量写入 + checkpoint + 重开全量验证
* 2. 混合操作(insert/update/delete5 万级 + 崩溃模拟(不 checkpoint 断开)→ 重开验证已确认写入
* 3. 多 checkpoint 循环下数据不丢
*/
import { KVStoreEngine } from '../../src/engine/kvstore_engine';
import { SharedMemoryBackend } from '../../src/engine/kvstore/shared_memory_medium';
import { createSchema } from '../../src/table/schema';
let counter = 0;
function uniqueDB(): string {
return `stress-${Date.now()}-${++counter}-${Math.random().toString(36).slice(2, 6)}`;
}
beforeEach(() => {
SharedMemoryBackend.clearRegistry();
});
describe('KVStoreEngine — 10 万级压力', () => {
it('10 万 key 写入 + checkpoint + 重开全量验证', async () => {
const dbName = uniqueDB();
const engine = new KVStoreEngine(new SharedMemoryBackend());
await engine.open(dbName, 1);
await engine.createTable(createSchema('big', {
id: { type: 'string', primaryKey: true },
val: { type: 'number' },
name: { type: 'string' },
}));
const TOTAL = 100000;
// 分批写入(每批 1000
for (let batch = 0; batch < TOTAL / 1000; batch++) {
const rows = [] as Record<string, unknown>[];
for (let i = 0; i < 1000; i++) {
const idx = batch * 1000 + i;
rows.push({ id: `k${idx}`, val: idx, name: `User${idx}` });
}
await engine.insert('big', rows);
// 每 20 批 checkpoint 一次
if ((batch + 1) % 20 === 0) {
await (engine as any).kv.checkpoint();
}
}
expect(await engine.count('big')).toBe(TOTAL);
await engine.close();
// 重开:全量验证
const engine2 = new KVStoreEngine(new SharedMemoryBackend());
await engine2.open(dbName, 1);
expect(await engine2.count('big')).toBe(TOTAL);
// 抽样验证
for (const id of ['k0', 'k49999', 'k99999', 'k12345']) {
const rows = await engine2.find('big', { table: 'big', where: { id } });
expect(rows).toHaveLength(1);
expect(Number(rows[0].val)).toBe(Number(id.slice(1)));
}
await engine2.close();
}, 120000);
it('5 万混合操作 + 崩溃模拟(不 checkpoint)→ 重开全部已确认写入可见', async () => {
const dbName = uniqueDB();
const engine = new KVStoreEngine(new SharedMemoryBackend());
await engine.open(dbName, 1);
await engine.createTable(createSchema('ops', {
id: { type: 'string', primaryKey: true },
val: { type: 'number' },
}));
let seed = 42;
const rand = () => { seed = (seed * 1103515245 + 12345) & 0x7fffffff; return seed / 0x7fffffff; };
const confirmed = new Map<string, number>();
const TOTAL = 50000;
for (let i = 0; i < TOTAL; i++) {
const r = rand();
const id = `k${Math.floor(rand() * 20000)}`;
if (r < 0.6) {
const val = Math.floor(rand() * 1000000);
try {
await engine.insert('ops', [{ id, val }]);
confirmed.set(id, val);
} catch (e) {
if ((e as { code?: string }).code !== 'DUPLICATE_KEY') throw e;
}
} else if (r < 0.8) {
const val = Math.floor(rand() * 1000000);
await engine.update('ops', { table: 'ops', where: { id } }, { val });
if (confirmed.has(id)) confirmed.set(id, val);
} else {
await engine.delete('ops', { table: 'ops', where: { id } });
confirmed.delete(id);
}
// 每 5000 次 checkpoint
if ((i + 1) % 5000 === 0) {
await (engine as any).kv.checkpoint();
}
}
// 崩溃模拟:直接断开(不 close → 数据在 KVStore 日志/快照)
await engine.close();
const engine2 = new KVStoreEngine(new SharedMemoryBackend());
await engine2.open(dbName, 1);
expect(await engine2.count('ops')).toBe(confirmed.size);
// 抽样验证值
let sampled = 0;
for (const [id, val] of confirmed) {
if (sampled++ > 1000) break;
const rows = await engine2.find('ops', { table: 'ops', where: { id } });
expect(rows[0].val).toBe(val);
}
await engine2.close();
}, 120000);
it('多次 checkpoint 循环(500 次)数据不丢', async () => {
const dbName = uniqueDB();
const engine = new KVStoreEngine(new SharedMemoryBackend(), 0);
await engine.open(dbName, 1);
await engine.createTable(createSchema('t', {
id: { type: 'string', primaryKey: true },
v: { type: 'number' },
}));
for (let i = 0; i < 500; i++) {
await engine.insert('t', [{ id: `k${i}`, v: i }]);
await (engine as any).kv.checkpoint();
}
await engine.close();
const engine2 = new KVStoreEngine(new SharedMemoryBackend(), 0);
await engine2.open(dbName, 1);
expect(await engine2.count('t')).toBe(500);
const last = await engine2.find('t', { table: 't', where: { id: 'k499' } });
expect(last[0].v).toBe(499);
await engine2.close();
}, 60000);
});