Files
MetonaSqlark/tests/engine/aria-mvcc-savepoint.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

109 lines
3.9 KiB
TypeScript

/**
* AriaEngine MVCC 事务隔离 + Savepoint 测试
* 全部 MemoryBackend,零卡死
*/
import { AriaEngine } from '../../src/engine/aria/index';
import { createSchema } from '../../src/table/schema';
import { installOPFSMock } from '../helpers/opfs-mock';
beforeEach(() => { installOPFSMock(new Map()); });
describe('AriaEngine — MVCC 事务 + Savepoint', () => {
let engine: AriaEngine;
beforeEach(async () => {
engine = new AriaEngine({ storageBackend: 'memory' });
await engine.open('mvcc-test', 1);
await engine.createTable(createSchema('users', {
id: { type: 'string', primaryKey: true },
name: { type: 'string', required: true },
balance: { type: 'number', default: 0 },
}));
});
afterEach(async () => { await engine.close(); });
// ---- MVCC 事务 ----
it('beginTransaction 分配唯一事务ID', () => {
const id1 = (engine as any).mvcc.beginTransaction();
const id2 = (engine as any).mvcc.beginTransaction();
expect(id1).not.toBe(id2);
});
it('commit 后版本标记已提交', () => {
const txnId = (engine as any).mvcc.beginTransaction();
(engine as any).mvcc.writeVersion('users', '1', { name: 'Alice' }, txnId);
(engine as any).mvcc.commitTransaction(txnId);
const store = (engine as any).mvcc.versionStore;
expect(store.get('users.1')[0].committed).toBe(true);
});
it('rollback 移除本事务版本', () => {
const txnId = (engine as any).mvcc.beginTransaction();
(engine as any).mvcc.writeVersion('users', '1', { name: 'Alice' }, txnId);
(engine as any).mvcc.rollbackTransaction(txnId);
const store = (engine as any).mvcc.versionStore;
expect(store.has('users.1')).toBe(false);
});
it('引擎层事务 commit 后数据可见', async () => {
await engine.beginTransaction();
await engine.insert('users', [{ id: '1', name: 'Alice', balance: 100 }]);
await engine.commitTransaction();
expect(await engine.count('users')).toBe(1);
});
it('引擎层事务 rollback 后数据消失', async () => {
await engine.insert('users', [{ id: '1', name: 'Alice' }]);
await engine.beginTransaction();
await engine.insert('users', [{ id: '2', name: 'Bob' }]);
await engine.rollbackTransaction();
expect(await engine.count('users')).toBe(1);
});
it('GC 清理过旧版本(保留最新 N 个)', () => {
for (let i = 0; i < 200; i++) {
const txnId = (engine as any).mvcc.beginTransaction();
(engine as any).mvcc.writeVersion('users', '1', { ver: i }, txnId);
(engine as any).mvcc.commitTransaction(txnId);
}
(engine as any).mvcc.gc(50);
expect((engine as any).mvcc.versionStore.get('users.1').length).toBe(50);
});
// ---- Savepoint ----
it('savepoint 创建成功', async () => {
await engine.beginTransaction();
await (engine as any).savepoint('sp1');
expect((engine as any).savepoints.has('sp1')).toBe(true);
await engine.rollbackTransaction();
});
it('rollbackToSavepoint 恢复快照', async () => {
await engine.insert('users', [{ id: '1', name: 'Alice' }]);
await engine.beginTransaction();
await engine.insert('users', [{ id: '2', name: 'Bob' }]);
await (engine as any).savepoint('sp1');
await engine.insert('users', [{ id: '3', name: 'Charlie' }]);
await (engine as any).rollbackToSavepoint('sp1');
await engine.commitTransaction();
expect(await engine.count('users')).toBe(2);
});
it('releaseSavepoint 释放后不可回滚', async () => {
await engine.beginTransaction();
await (engine as any).savepoint('sp1');
await (engine as any).releaseSavepoint('sp1');
expect((engine as any).savepoints.has('sp1')).toBe(false);
await engine.rollbackTransaction();
});
it('重复 savepoint 名称抛出错误', async () => {
await engine.beginTransaction();
await (engine as any).savepoint('sp1');
await expect((engine as any).savepoint('sp1')).rejects.toThrow('already exists');
await engine.rollbackTransaction();
});
});