140 lines
4.9 KiB
TypeScript
140 lines
4.9 KiB
TypeScript
/**
|
||
* KVStoreEngine — 大规模压力测试(10 万级)
|
||
*
|
||
* 验证生产级可靠性:
|
||
* 1. 10 万 key 批量写入 + checkpoint + 重开全量验证
|
||
* 2. 混合操作(insert/update/delete)5 万级 + 崩溃模拟(不 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);
|
||
});
|