Persistence — fork, CoW, and the 1-second window

RDB forks a child that writes the dataset via copy-on-write, and AOF's everysec fsync trades up to one second of writes for throughput — together they define what 'durable Redis' actually means.

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

  1. Watch
  2. Try it
  3. Predict
  4. Capture
fork pause: idle4KB pagesRedis parentserving clientsPAGE TABLE · regular pages (4KB)shared (CoW)copieddirtywritesRSS240 MBAOF TAIL · buffer → fsync → diskappendfsynceverysecfsync ≤ 1sLost writes0none
fsync once per second · THP OFF (4 KB CoW unit)
What to watch for

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.

Continue unlocks when the animation finishes.
Implementation

Highlighted lines are the ones running in the diagram right now.

Server.bgsave()
fork a child; parent keeps serving on copy-on-write pages
def bgsave():
pid = fork()
if pid == 0:
# child: frozen view of memory at fork time
writeRDB(snapshot=heap_snapshot())
exit(0)
# parent returns immediately; serves clients
# while pages diverge via CoW on each write.
return pid
Server.handleWrite(cmd)
mutate memory, append to AOF buffer, reply OK
def handleWrite(cmd):
apply(cmd, db) # in-memory mutation
aof_buf.append(cmd) # OS page-cache write
if cfg.appendfsync == 'always':
fsync(aof_fd) # block until on disk
reply_ok(client) # ack returns to caller
aofFlushBackground() # everysec
background thread fsyncs the AOF once per second
def aofFlushBackground():
last_fsync = now()
while running:
if now() - last_fsync >= 1.0:
fsync(aof_fd)
last_fsync = now()
sleep(0.1)
kernel.pageWrite(page)
first parent write to a shared page triggers the copy
def pageWrite(page):
if page.shared:
new = copy_page(page) # 4 KB, or 2 MB under THP
page.shared = False
mark_copied(page, new)
write_into(page)

Where this sits in Build Redis

Scene 04 of 10, in the Memory act — Persistence, eviction, TTL — what makes RAM disappear.. RDB snapshots via fork+CoW, AOF fsync policies, and why default Redis can lose ~1s of writes on crash.

Up next. Eviction — persistence handles the crash. But while the process is running and RAM fills up, who has to leave to make room for the next write?

All 10 scenes in Build Redis · Every curriculum

Built with Arqly
Every scene in Build Redis builds on the one before it.All 10 Build Redis scenes