mpackdb
All repositories: gitoria
2.6 KB
import MPackDB from '../src/MPackDB.js';import { rm } from 'fs/promises';const TEST_DB_PATH = 'db/concurrent-delete-during-scan';console.log('--- Concurrent Delete During Scan Test ---');console.log('Regression test for the refresh() clobber bug (BUG_REPORT.md):');console.log('an unlocked find() must not roll back un-persisted meta mutations');console.log('of a concurrent delete()/insert() in the same process.\n');await rm(TEST_DB_PATH, { recursive: true, force: true });// UTXO-like store: string primary key + secondary indexes, like the blockchain usesconst db = new MPackDB(`${TEST_DB_PATH}/utxos`, {primaryKey: 'id',indexes: ['symbol', '*blockNumber'],});const ROUNDS = 40;let resurrections = 0;let lostInserts = 0;for (let round = 0; round < ROUNDS; round++) {const spentId = `spent-${round}`;const keptId = `kept-${round}`;await db.insert({ id: spentId, symbol: 'ADR', blockNumber: round, v: 1 });// Hammer scans while deleting, so a scan's refresh() lands inside delete()'s await windowconst hammer = (async () => {for (let i = 0; i < 25; i++) {for await (const _ of db.find(() => true)) { /* full scan */ }}})();await Promise.all([db.delete(spentId),db.insert({ id: keptId, symbol: 'ADR', blockNumber: round, v: 2 }),hammer,]);const spent = await db.find(spentId);if (spent.length !== 0) {resurrections++;console.log(` round ${round}: ✗ deleted record '${spentId}' RESURRECTED (${spent.length} copies)`);}const kept = await db.find(keptId);if (kept.length !== 1) {lostInserts++;console.log(` round ${round}: ✗ inserted record '${keptId}' wrong count: ${kept.length} (expected 1)`);}}// Also verify against a full unindexed scan (bypasses index paths)const all = await db.find(() => true);const spentAlive = all.filter(r => r.id.startsWith('spent-'));const keptAlive = all.filter(r => r.id.startsWith('kept-'));console.log(`\nRounds: ${ROUNDS}`);console.log(`Resurrected deletes (PK lookup): ${resurrections} (expected: 0)`);console.log(`Lost inserts (PK lookup): ${lostInserts} (expected: 0)`);console.log(`Deleted records in full scan: ${spentAlive.length} (expected: 0)`);console.log(`Kept records in full scan: ${keptAlive.length} (expected: ${ROUNDS})`);await db.close();const pass = resurrections === 0 && lostInserts === 0 && spentAlive.length === 0 && keptAlive.length === ROUNDS;console.log(`\n${pass ? '✓ All concurrent delete-during-scan tests passed!' : '✗ FAILED — concurrent scans corrupted meta'}`);if (!pass) {throw new Error('concurrent-delete-during-scan FAILED: deletes resurrected or inserts lost during concurrent scans');}
Branches
- mastermain branch
Latest commits
- 87888725release 1.0.7caramboleyo
- c4cdb9b6node: import prefixes (Deno compat) + pre-existing index-state WIPcaramboleyo
- 0afb8f4bupdate now must be a callbackcaramboleyo
- cde73eb4release 1.0.6caramboleyo
- d01dda02add index hints, intersection, boundingBox; remove findByIndexcaramboleyo
- b8ffc1a0release 1.0.5caramboleyo
- d47876a1reimplemented lost features like indexed find and more testscaramboleyo
- 7f08da9afixed insert ignoring model definitioncaramboleyo
- 705774a9added flush before findcaramboleyo
- b4db6391initial commitcaramboleyo