mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-08-17 20:55:34 +00:00
- add @moonshot-ai/minidb: pure-Node.js embedded KV store mixing Redis-style in-memory access with WAL + snapshot persistence - implement core engine: codec, store, wal, snapshot, recovery, compaction, lockfile, skiplist - add indexing: dt-index, compound-index, text-index/postings, index-manager, query engine - add server, value-reader, crc32 utilities - include unit/e2e tests plus fuzz/crash-recovery/soak suites and session-store benchmarks - register package in flake.nix and pnpm-lock.yaml
112 lines
4 KiB
TypeScript
112 lines
4 KiB
TypeScript
// test/batch.test.js
|
|
import { test } from 'vitest';
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs/promises';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import { MiniDb } from '../src/index.js';
|
|
|
|
async function tmpDir() {
|
|
return fs.mkdtemp(path.join(os.tmpdir(), 'minidb-batch-'));
|
|
}
|
|
|
|
test('batch applies multiple ops atomically', async () => {
|
|
const dir = await tmpDir();
|
|
const db = await MiniDb.open({ dir, valueCodec: 'json' });
|
|
try {
|
|
await db.batch([
|
|
{ op: 'set', key: 'a', value: { n: 1 } },
|
|
{ op: 'set', key: 'b', value: { n: 2 } },
|
|
{ op: 'del', key: 'a' },
|
|
]);
|
|
assert.equal(db.get('a'), undefined);
|
|
assert.deepEqual(db.get('b'), { n: 2 });
|
|
assert.equal(db.size, 1);
|
|
} finally {
|
|
await db.close();
|
|
await fs.rm(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test('batch with dt + indexes updates everything', async () => {
|
|
const dir = await tmpDir();
|
|
const db = await MiniDb.open({ dir, valueCodec: 'json' });
|
|
await db.createIndex('byCity', { field: 'city' });
|
|
try {
|
|
await db.batch([
|
|
{ op: 'set', key: 'u1', value: { city: 'Paris' }, dt: { created: 100 } },
|
|
{ op: 'set', key: 'u2', value: { city: 'Paris' }, dt: { created: 200 } },
|
|
]);
|
|
assert.deepEqual(db.findEq('byCity', 'Paris').map((r) => r.key).sort(), ['u1', 'u2']);
|
|
assert.deepEqual(db.dtRange('created', { gte: 100 }).map((r) => r.key).sort(), ['u1', 'u2']);
|
|
} finally {
|
|
await db.close();
|
|
await fs.rm(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test('batch unique violation rejects the whole batch', async () => {
|
|
const dir = await tmpDir();
|
|
const db = await MiniDb.open({ dir, valueCodec: 'json' });
|
|
await db.createIndex('byEmail', { field: 'email', unique: true });
|
|
await db.set('a', { email: 'x@y.com' });
|
|
try {
|
|
await assert.rejects(
|
|
() => db.batch([{ op: 'set', key: 'b', value: { email: 'ok@y.com' } }, { op: 'set', key: 'c', value: { email: 'x@y.com' } }]),
|
|
/unique/,
|
|
);
|
|
// neither op applied
|
|
assert.equal(db.get('b'), undefined);
|
|
assert.equal(db.get('c'), undefined);
|
|
assert.equal(db.size, 1);
|
|
} finally {
|
|
await db.close();
|
|
await fs.rm(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test('batch is atomic on recovery: a corrupt batch frame skips the whole batch', async () => {
|
|
const dir = await tmpDir();
|
|
try {
|
|
let db = await MiniDb.open({ dir, valueCodec: 'json', fsyncPolicy: 'always', autoCompact: false });
|
|
await db.set('before', { v: 0 });
|
|
await db.batch([
|
|
{ op: 'set', key: 'x', value: { v: 1 } },
|
|
{ op: 'set', key: 'y', value: { v: 2 } },
|
|
]);
|
|
await db.set('after', { v: 3 });
|
|
await db.close();
|
|
|
|
// Corrupt the batch frame (the 2nd frame, between 'before' and 'after').
|
|
const wal = path.join(dir, 'db.wal');
|
|
const buf = await fs.readFile(wal);
|
|
const magic = Buffer.from([0x4d, 0x44]);
|
|
const second = buf.indexOf(magic, buf.indexOf(magic) + 1); // start of batch frame
|
|
buf[second + 30] ^= 0xff; // flip a byte inside the batch body -> bad crc
|
|
await fs.writeFile(wal, buf);
|
|
|
|
db = await MiniDb.open({ dir, valueCodec: 'json', recovery: 'resync' });
|
|
assert.equal(db.get('before').v, 0, 'before survives');
|
|
// the batch is either fully skipped or (if resync lands inside) neither x nor y appears
|
|
// — but it must NEVER be the case that only one of x/y is present.
|
|
const x = db.get('x');
|
|
const y = db.get('y');
|
|
assert.ok(!(x !== undefined && y === undefined), 'batch must not be half-applied (x only)');
|
|
assert.ok(!(x === undefined && y !== undefined), 'batch must not be half-applied (y only)');
|
|
await db.close();
|
|
} finally {
|
|
await fs.rm(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test('empty batch is a no-op', async () => {
|
|
const dir = await tmpDir();
|
|
const db = await MiniDb.open({ dir, valueCodec: 'json' });
|
|
try {
|
|
await db.batch([]);
|
|
assert.equal(db.size, 0);
|
|
} finally {
|
|
await db.close();
|
|
await fs.rm(dir, { recursive: true, force: true });
|
|
}
|
|
});
|