Files
MetonaSqlark/tests/engine/kvstore-stress.test.ts
T
thzxx 05e6823bf1
CI / test (22.x) (push) Successful in 24m26s
CI / e2e (push) Successful in 10m0s
CI / test (18.x) (push) Successful in 27m14s
CI / test (20.x) (push) Failing after 1h19m9s
CI / test (24.x) (push) Successful in 37m49s
fix: v0.7.2 语句级原子性 + 事务 DDL 拒绝 + 约束/绑定硬化 — 6 项修复 + 43 回归 + CI 重型套件串行
- UPDATE 语句级部分提交(P1,四引擎):两阶段全量预检后执行,批内唯一互查,
  任何一行失败整句不执行(aria 场景 WAL 与内存不再错位)
- 事务内 ALTER/CREATE INDEX/DROP INDEX 残留(P1):Memory/KVStore 显式拒绝
  (对齐 Aria),createTable/dropTable 保持可回滚
- SET NULL 级联绕过 required 约束(P1):预检阶段整体拒绝 FOREIGN_KEY_VIOLATION
- bindParameters 注释误判(P2):行注释/块注释中的 ? 与引号不再参与绑定
- 未闭合字符串静默接受 → lexer 抛 PARSE_ERROR;未知 where 操作符抛 QUERY_ERROR
- UPDATE undefined 覆盖列值 → 语义化为不更新(null 仍置空)
- Hybrid 写穿透非原子(P1):磁盘失败自动重载内存对齐磁盘再抛原错误
- CI:Run tests 拆常规并行 + 重型串行(runInBand),重型测试超时余量提升,
  性能护栏 kv 120→240s / opfs 150→300s(仍拦截悬崖回归)
- 测试 1155 → 1198(74 套件),覆盖率 89.82% 保持
2026-08-13 15:31:16 +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();
}, 600000);
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();
}, 600000);
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();
}, 300000);
});