Build Redis (10 scenes)
Scene 04 · Persistence — fork, CoW, and the 1-second window
RDB snapshots via fork+CoW, AOF fsync policies, and why default Redis can lose ~1s of writes on crash.
Previously
You've seen what's in RAM and how it's encoded. None of it survives a crash on its own — persistence is how the live keyspace becomes bytes on disk.
Scene 04
Persistence — fork, CoW, and the 1-second window
Diagram
On the left, the live keyspace held in RAM, drawn as a page grid. RDB forks a child process — shown as a snapshot copy on the right — that writes the dump while the parent keeps serving; pages mutated during the snapshot get copied (CoW) and the memory gauge climbs. At the bottom, the AOF buffer accumulates writes and an fsync arrow drains it to disk on the policy you pick (always / everysec / no).
Watch writes land on the AOF tail (amber = buffered) and flip emerald when the everysec fsync fires. After a few seconds, BGSAVE forks a child — pages start flipping orange one by one as the parent keeps writing during the snapshot.
Implementation
Server.bgsave()
fork a child; parent keeps serving on copy-on-write pages
1def bgsave():2 pid = fork()3 if pid == 0:4 # child: frozen view of memory at fork time5 writeRDB(snapshot=heap_snapshot())6 exit(0)7 # parent returns immediately; serves clients8 # while pages diverge via CoW on each write.9 return pid
Server.handleWrite(cmd)
mutate memory, append to AOF buffer, reply OK
1def handleWrite(cmd):2 apply(cmd, db) # in-memory mutation3 aof_buf.append(cmd) # OS page-cache write4 if cfg.appendfsync == 'always':5 fsync(aof_fd) # block until on disk6 reply_ok(client) # ack returns to caller
aofFlushBackground() # everysec
background thread fsyncs the AOF once per second
1def aofFlushBackground():2 last_fsync = now()3 while running:4 if now() - last_fsync >= 1.0:5 fsync(aof_fd)6 last_fsync = now()7 sleep(0.1)
kernel.pageWrite(page)
first parent write to a shared page triggers the copy
1def pageWrite(page):2 if page.shared:3 new = copy_page(page) # 4 KB, or 2 MB under THP4 page.shared = False5 mark_copied(page, new)6 write_into(page)