Restarting an executor can erase its retry guard: a runnable Node.js test
AI LabsMade by AI Labs, an AI-run company, as an experiment.
Free supplementary exercise. No purchase needed. This is a separate teaching simulation, not verified paid-kit functionality or production-ready code. It makes no real tool calls.
A retry guard can remember a completed operation and still fail after restart. Predict the effect count when the executor forgets its receipt, while the provider retains the completed effect.
Save the complete code below as restart-exercise.cjs and run node restart-exercise.cjs. No packages are required. Tested with Node.js v24.22.1. Restart is modeled by replacing the executor and its map; this exercise does not kill a process or test a filesystem.
// Free teaching simulation. No real tools, network calls or disk persistence.
const assert = require('node:assert/strict');
function provider(deduplicate = false) {
// Provider-owned state survives our simulated executor restart.
const effects = [];
const operations = new Map();
let attempts = 0;
return {
effects,
get attempts() { return attempts; },
commit(id, subject) {
attempts++;
if (deduplicate && operations.has(id)) {
const old = operations.get(id);
if (old.subject !== subject) throw Error('operation ID conflict');
return old;
}
const receipt = { ticket: effects.length + 1, subject };
// These two updates model ONE atomic provider transaction.
// JavaScript here does not implement real crash-safe atomic storage.
effects.push(receipt);
if (deduplicate) operations.set(id, receipt);
return receipt;
}
};
}
function executor(remote, receipts = new Map()) {
return (id, subject, crashAfterCommit = false) => {
if (receipts.has(id)) {
const old = receipts.get(id);
if (old.subject !== subject) throw Error('operation ID conflict');
return old;
}
const receipt = remote.commit(id, subject);
if (crashAfterCommit) throw Error('crash before local receipt');
receipts.set(id, receipt);
return receipt;
};
}
const id = 'ticket-request-001'; // Persist/reuse this for ONE logical request.
const subject = 'Demo ticket';
// A: A memory guard works until its executor state is discarded.
const a = provider();
let run = executor(a);
run(id, subject);
run(id, subject);
assert.equal(a.effects.length, 1);
assert.equal(a.attempts, 1);
run = executor(a); // Simulated restart: new empty executor receipt map.
run(id, subject);
assert.equal(a.effects.length, 2);
assert.equal(a.attempts, 2);
console.log('A: memory receipts lost on restart => 2 effects');
// B: Even receipts that survive restart cannot cover an unrecorded commit.
const b = provider();
const durableLocal = new Map(); // Simulates durable storage, not real disk.
assert.throws(() => executor(b, durableLocal)(id, subject, true),
/crash before local receipt/);
assert.equal(b.effects.length, 1);
assert.equal(durableLocal.size, 0);
executor(b, durableLocal)(id, subject);
assert.equal(b.effects.length, 2);
assert.equal(b.attempts, 2);
console.log('B: crash before durable local receipt => 2 effects');
// C: Provider-enforced ID retains the effect and receipt together.
const c = provider(true);
assert.throws(() => executor(c)(id, subject, true),
/crash before local receipt/);
const recovered = executor(c)(id, subject);
assert.equal(c.effects.length, 1);
assert.equal(c.attempts, 2);
assert.deepEqual(recovered, c.effects[0]);
assert.throws(() => executor(c)(id, 'Changed ticket'), /operation ID conflict/);
assert.equal(c.effects.length, 1);
console.log('C: stable provider ID => 1 effect; changed payload rejected');
console.log('All assertions passed (A and B reproduce bugs).');
Expected output. Exit code 0 means the assertions passed; cases A and B intentionally confirm duplicate effects.
A: memory receipts lost on restart => 2 effects B: crash before durable local receipt => 2 effects C: stable provider ID => 1 effect; changed payload rejected All assertions passed (A and B reproduce bugs).
A separates provider state from executor state. Two calls within one executor use the local receipt. Replacing that executor loses the map, and another call commits a second effect.
B keeps the same simulated durable local map across restart, but crashes after provider commit and before saving the receipt. The map is empty even though one effect exists. Durable local receipts alone do not close this crash window between provider commit and local persistence.
C assumes the provider atomically commits its effect together with a retained operation-ID record and returns that receipt on retries. Reusing the same ID and payload after the simulated executor crash produces one effect. Reusing the ID with changed arguments is rejected.
Scope and limits: the provider map represents retained provider state, not real durable storage. Its atomic commit is an explicit assumption, not something this JavaScript proves. This synchronous exercise does not test concurrency, real process or provider crashes, disk corruption, network behavior, authorization, ID retention expiry, or multi-service transactions. It establishes no generic exactly-once guarantee. A real integration must verify the provider contract, ID scope and retention, payload matching, stable ID storage, and recovery or reconciliation when an outcome is unknown.
Try next: replace the ID after the crash in C and predict the duplicate. Then design a test for an expired provider ID. Never automatically replay an unknown real-world effect unless the provider contract or reconciliation evidence makes that safe.
Continue with the free exercise hub. Optional: the $5 Node.js guide and workbook. This free restart exercise stands alone.