AOF with fs.createWriteStream
Before the code: what crash-safety actually means
Your store is fast because it lives in memory. The price of that speed is that a single power cut wipes everything. This module fixes that. The trick is simple: keep a flight recorder. Every write the server accepts is appended to a file on disk. After a crash, you replay the file from the top and the server is back. That is the entire append-only log idea, used inside Redis, every SQL database, Kafka, and even git.
Write to memory, log to disk
Quiz: Quiz
Loading practice…
Open the file once with createWriteStream({ flags: 'a' }) and keep the handle. Every write goes through OS buffering with minimal syscalls.
function aofAppend(args) {
if (!aofStream) aofStream = createWriteStream(AOF_PATH, { flags: "a" });
aofStream.write(encodeArray(args));
}aofAppend writes via the stream. No open + close per call. encodeArray is the same RESP array encoder from the store lessons; the step file names it encodeRESPArray, but it is one helper, so we keep one name here.
One refactor before replay makes sense: dispatch now goes through a wrapper. handleCommand(frame, persist) looks up the handler in the COMMANDS object, runs it, and, when persist is true and the command is a successful write, calls aofAppend. Replay passes persist as false. Without that flag, replaying the log would append every command back onto the log it came from, doubling the file on every restart.
function aofReplay() {
if (!existsSync(AOF_PATH)) return { replayed: 0, dropped: 0 };
const buf = readFileSync(AOF_PATH);
let cursor = 0, replayed = 0, dropped = 0;
while (cursor < buf.length) {
let r;
try { r = parseResp(buf, cursor); }
catch (e) { dropped = buf.length - cursor; break; }
if (r === null) { dropped = buf.length - cursor; break; }
if (Array.isArray(r.value)) {
handleCommand(r.value, false);
replayed++;
}
cursor += r.consumed;
}
return { replayed, dropped };
}Replay uses the same parseResp. Partial trailing data is detected and skipped.