RDB: Atomic rename without fork
C Redis forks for BGSAVE; the child inherits memory via copy-on-write. tokio cannot fork after the runtime spawns tasks. We clone the HashMap under the lock instead. Peak memory is briefly 2x; the architectural lesson (atomic rename) is identical.
async fn snapshot_atomic(store: &Store) -> io::Result<usize> {
let snap: HashMap<String, String> = { store.lock().await.store.clone() };
let count = snap.len();
let json = serde_json::to_vec(&snap).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
tokio::fs::write(RDB_TMP, &json).await?;
tokio::fs::rename(RDB_TMP, RDB_PATH).await?;
Ok(count)
}Clone under the lock, then drop the lock before writing. tokio::fs::rename is atomic on POSIX.
async fn rdb_load(store: &Store) -> io::Result<usize> {
let path = PathBuf::from(RDB_PATH);
if !tokio::fs::try_exists(&path).await? { return Ok(0); }
let bytes = tokio::fs::read(&path).await?;
let map: HashMap<String, String> = serde_json::from_slice(&bytes)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
let n = map.len();
store.lock().await.store = map;
Ok(n)
}On startup, read the RDB and replace the in-memory map.
Clone-under-lock vs fork+COW
Quiz: Quiz
Loading practice…