From 01819642cc6d6b488b4deb6c16f084c180074838 Mon Sep 17 00:00:00 2001 From: pptx704 Date: Sun, 3 May 2026 14:27:49 +0600 Subject: [PATCH] fix: drop page cache before snapshot to reduce memory dump size Linux keeps freed memory as page cache, which Firecracker snapshots as non-zero blocks. A 16GB VM with 12GB stale cache would write all 12GB to disk. Dropping pagecache (not dentries/inodes) in /snapshot/prepare before blocking the reclaimer shrinks snapshots to actual working set size with minimal resume latency impact. --- envd-rs/src/http/snapshot.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/envd-rs/src/http/snapshot.rs b/envd-rs/src/http/snapshot.rs index 977b6bd..e507d8f 100644 --- a/envd-rs/src/http/snapshot.rs +++ b/envd-rs/src/http/snapshot.rs @@ -10,12 +10,22 @@ use crate::state::AppState; /// POST /snapshot/prepare — quiesce subsystems before Firecracker snapshot. /// /// In Rust there is no GC dance. We just: -/// 1. Stop port subsystem -/// 2. Close idle connections via conntracker -/// 3. Set needs_restore flag +/// 1. Drop page cache to shrink snapshot size +/// 2. Stop port subsystem +/// 3. Close idle connections via conntracker +/// 4. Set needs_restore flag pub async fn post_snapshot_prepare(State(state): State>) -> impl IntoResponse { - // Block memory reclaimer before anything else — prevents drop_caches - // from running mid-freeze which would corrupt kernel page table state. + // Drop page cache BEFORE blocking the reclaimer — avoids snapshotting + // gigabytes of stale cache that inflates the memory dump on disk. + // "1" = pagecache only (keep dentries/inodes for faster resume). + if let Err(e) = std::fs::write("/proc/sys/vm/drop_caches", "1") { + tracing::warn!(error = %e, "snapshot/prepare: drop_caches failed"); + } else { + tracing::info!("snapshot/prepare: page cache dropped"); + } + + // Block memory reclaimer — prevents drop_caches from running mid-freeze + // which would corrupt kernel page table state. state.snapshot_in_progress.store(true, Ordering::Release); if let Some(ref ps) = state.port_subsystem {