RDB: fs.writeFile + fs.rename

JSON.stringify + writeFile + rename. The save is async; BGSAVE returns immediately and the event loop keeps serving. No fork, no worker_threads needed for small to medium stores.

Atomic snapshot via tmp file plus rename

BGSAVE returns immediately. The async write lands on disk, then a single rename swaps the file in place.
06-rdb-snapshots/server.mjs
javascript
async function rdbSave() {
  const tmp = RDB_PATH + ".tmp";
  const payload = {
    version: 1, saved_at: Math.floor(Date.now() / 1000),
    store: Object.fromEntries(STORE),
    expires: Object.fromEntries(EXPIRES),
  };
  await writeFile(tmp, JSON.stringify(payload));
  await rename(tmp, RDB_PATH);
  lastSave = payload.saved_at;
}

rdbSave is async. fs.rename is atomic. lastSave tracks success.

Worth knowing: real Redis takes its snapshot by calling fork(). The child process gets a copy-on-write view of memory and writes the file while the parent keeps serving clients. We skip that trick on purpose. By the time your script runs, Node has already spawned libuv helper threads, and fork() carries only the calling thread into the child. The other threads vanish mid-flight, which can leave internal locks held forever and deadlock the child. So in Node, BGSAVE is just an async function on the event loop.

Quiz: Quiz

Loading practice…