RDB: snapshot-then-save without fork

C Redis forks for BGSAVE. The child inherits memory via copy-on-write and writes the snapshot while the parent serves. We cannot do that in Go because fork after the runtime has started other goroutines is unsafe.

06-rdb-snapshots/main.go
go
func (s *Store) Snapshot() (map[string]string, map[string]int64) {
    s.mu.Lock(); defer s.mu.Unlock()
    mc := make(map[string]string, len(s.m))
    ec := make(map[string]int64, len(s.expires))
    for k, v := range s.m { mc[k] = v }
    for k, ts := range s.expires { ec[k] = ts.Unix() }
    return mc, ec
}

Snapshot takes the lock just long enough to copy the maps. The save runs against the copies without holding the lock.

06-rdb-snapshots/main.go
go
case "BGSAVE":
    go func() {
        if err := rdbSave(); err != nil { log.Printf("[bgsave] %v", err) }
    }()
    return encodeSimple("Background saving started")

BGSAVE returns immediately. The save runs in a goroutine against the snapshot.

Snapshot-then-save vs fork+COW

Go pays memory at snapshot time. C pays memory only for pages the parent dirties during save.

Quiz: Quiz

Loading practice…