fix(placement,fc): memory-unaware scheduling risks host OOM; balloon DeflateOnOom should be true
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 1.6k
- Forks
- 438
- PR merge metrics
- No merged PRs in 30d
Description
Background
A sandbox created with ram_mb=2048 gets Firecracker MemSizeMib=2048 — an absolute hard ceiling imposed by the VMM. The guest kernel sees exactly that much physical RAM and cannot exceed it; when it tries, the in-guest OOM killer fires. There are three compounding gaps that make this worse than necessary.
Gap 1 — Placement ignores memory (host OOM risk)
The BestOfK scoring function and its sample() filter are purely CPU-aware:
// placement_best_of_K.go
totalCapacity := config.R * cpuCount // only CPU
return (cpuRequested + float64(reserved) + config.Alpha*usageAvg) / totalCapacity
MemoryAllocatedBytes and MemoryTotalBytes are tracked per node but never used in placement decisions. A node with 64 GiB RAM can receive unlimited 2 GiB sandboxes until the host kernel OOMs and kills Firecracker processes indiscriminately.
CPU has an explicit overcommit ratio (BestOfKMaxOvercommit, default 4×). Memory has no equivalent guard.
Fix: add a BestOfKMaxMemoryOvercommit feature flag (default 0 = disabled) and:
- Hard filter in
sample(): skip nodes whereMemoryAllocatedBytes + requested > M × MemoryTotalBytes - Soft term in
Score(): prefer nodes with available memory headroom
Gap 2 — Balloon DeflateOnOom=false causes avoidable in-guest OOMs
The virtio-balloon device is installed with:
// fc/client.go
deflateOnOom := false
Free-page hinting/reporting is enabled (good), but when the guest approaches its memory ceiling, the balloon is not automatically deflated to return previously reclaimed pages to the guest. A guest that has had idle pages harvested via FPH will OOM-kill user processes even though the host could give those pages back instantly by deflating the balloon.
Fix: set DeflateOnOom = true.
Gap 3 — Host cgroup has no memory limit (defense-in-depth gap)
Each Firecracker process runs in a per-sandbox cgroup at /sys/fs/cgroup/e2b/sbx-{id}. The cgroup correctly enables the memory controller and reads memory.current / memory.peak for observability, but never writes memory.max or memory.high:
// cgroup/manager.go — Create() only opens the directory and memory.peak FD
// memory.max is never set
If Firecracker itself has a bug that causes it to allocate beyond the guest's MemSizeMib on the host side, there is no second enforcement layer.
Fix (follow-up): write memory.high = MemSizeMib × 1.05 to the sandbox cgroup at creation time as a soft backstop.
Proposed changes (this issue tracks all three)
| # | Change | File(s) | Complexity |
|---|---|---|---|
| 1 | Memory-aware placement filter + score | placement_best_of_K.go, orchestrator.go, flags.go |
Low |
| 2 | DeflateOnOom = true |
fc/client.go |
Trivial |
| 3 | Cgroup memory.high enforcement |
cgroup/manager.go, sandbox creation |
Medium |
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with placement_best_of_K.go, orchestrator.go, and flags.go to trace the existing CPU filter, scoring, and feature-flag paths. Then inspect fc/client.go and cgroup/manager.go alongside sandbox creation to understand the balloon and cgroup setup. Done means memory-aware placement, DeflateOnOom enabled, and the proposed memory.high backstop are implemented across the named areas.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, linux
- Domain
- infrastructure
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 50/100