mvcc-snapshot-isolation-txn
The MVCC engine (mvcc_engine.js) implements snapshot isolation with these components:
A simple monotonic counter (++this._counter) that provides strictly increasing timestamps for both transaction begin and commit events. No locks needed because JavaScript's single-threaded execution model makes increment operations atomic.
Each key maintains an array of (txnId, beginTs, endTs, value) tuples sorted by beginTs descending (most recent first). A version is:
- beginTs: the commit timestamp when this version was created
- endTs: the commit timestamp when this version was superseded (or Infinity for the latest/live version)
- Visible to a transaction with snapshot S iff beginTs ≤ S < endTs
The commit() method is fully synchronous (no await points), making it atomic:
beginTs > our transaction's beginTs, another transaction committed a write to this key after our snapshot was taken → abortendTs to our commit timestamp) and prepend our new versionUses the first-committer-wins rule for snapshot isolation. Two concurrent transactions (their intervals [beginTs, commitTs] overlap) that write to the same key: the second one to commit sees the first one's version with beginTs > its own beginTs and aborts.
After every commit and abort, removes versions where endTs < the oldest active transaction's beginTs. Such versions are invisible to all active snapshots. The latest version (endTs = Infinity) is never removed.
// Core commit logic — lock-free, synchronous, atomic
commit(txn) {
if (txn.status !== 'active') return false;
// Read-only: instant commit
if (!txn.isReadWrite) { /* ... */ return true; }
const commitTs = this._oracle.next();
// Write-write conflict detection
for (const [key] of txn.writes) {
const chain = this._chains.get(key);
if (chain && chain.length > 0) {
for (const v of chain) {
if (this._committed.has(v.txnId)) {
if (v.beginTs > txn.beginTs) { // conflict!
// abort
return false;
}
break;
}
}
}
}
// Install versions
for (const [key, value] of txn.writes) {
// supersede previous version
for (const v of chain) {
if (this._committed.has(v.txnId)) {
v.endTs = commitTs;
break;
}
}
// add new version
chain.unshift(new Version(txn.id, commitTs, Infinity, value));
}
this._garbageCollect();
return true;
}
// GC: remove versions invisible to all active snapshots
_garbageCollect() {
let oldestActive = Infinity;
for (const [, txn] of this._activeTxns) {
if (txn.beginTs < oldestActive) oldestActive = txn.beginTs;
}
for (const [key, chain] of this._chains) {
const newChain = chain.filter(v => v.endTs >= oldestActive);
// ... update map
}
}
All **18 tests** pass with 0 failures. The test suite (`test_mvcc.js`) covers: | Test | What it verifies | |------|-----------------| | Basic read/write | Single txn reads own write, commits, chain has one version | | Read-only transaction | Instant commit, no version installed | | Read-your-writes | Txn sees its own local buffer before commit | | Write-write conflict detection | Second concurrent writer to same key aborts | | Concurrent writes (first-committer-wins) | 10 concurrent writers to same key: exactly 1 commits, 9 abort | | Abort leaves no artifacts | Aborted txn leaves no version in chain | | Snapshot isolation | Reader sees snapshot value even after writer commits new value | | Serial transactions | Non-overlapping writes both succeed | | GC with active reader | Old reader pins old versions; after reader aborts, GC cleans up | | Large-scale non-conflicting | 500 txns to distinct keys all commit | | No deadlocks under contention | 100 txns sharing keys — all complete (lock-free = no deadlock) | | Write same key twice | Local write buffer: last write wins per txn | | Read undefined key | Missing key returns `undefined` | | Read then write | Read-then-write pattern, verify snapshot visibility | | **10k concurrent transactions** | **10,000 txns** (5k read-write + 5k read-only) all commit successfully | | Snapshot isolation + concurrent writers | Reader's snapshot not affected by concurrent writer | | Cleanup after aborted writer | Aborted txn's pending write cleans up completely | | Multiple aborts no leak | 50 aborts, then a normal commit succeeds cleanly | ### Key edge cases tested: - **First-committer-wins**: When 10 transactions concurrently write to `hotspot`, exactly 1 succeeds (the first to call `commit()`), the other 9 detect `latestCommitted.beginTs > own.beginTs` and abort - **Snapshot isolation**: A reader that began before a writer commits continues to see the old value even after the writer commits - **GC correctness**: Old active readers pin historical versions; when all readers at a snapshot finish, stale versions are reclaimed - **No deadlocks**: The lock-free protocol (no mutexes, no blocking) guarantees forward progress — 100% of 100 contended transactions completed - **10k transactions**: All 10,000 transactions (5,000 writers to unique keys + 5,000 readers) committed without error ---
{"model": "claude-sonnet-4-20250514", "problem_class": "mvcc-snapshot-isolation-txn", "result": "passed", "tests": 18}