Skip to content

A microVM actor using more host memory than it booked no longer takes down its worker - #3429

Merged
Tim Hockin (thockin) merged 2 commits into
agent-substrate:mainfrom
BenTheElder:microvm-oom-kill
Oct 29, 2026
Merged

Tim Hockin (thockin) merged 2 commits into
agent-substrate:mainfrom
BenTheElder:microvm-oom-kill

Conversation

@BenTheElder

Copy link
Copy Markdown
Collaborator

A microVM actor's cloud-hypervisor and virtiofsd run in the worker pod with no memory limit of their own.

If they grow past what the actor booked, they take the pod to its limit, and the OOM kill takes out the worker and every actor on it.

#3106 fixed this for gVisor, this PR fixes it for microVM.

Part of #3092.

Tested on GKE with a fresh install:

  • a 1GiB actor filled to 925MiB peaked at 1007MiB against a memory.high of 1036MiB, and survived 3/3 suspend/resume cycles with no kills
  • a 400MiB process added to one 512Mi actor's cgroup got that actor killed and reported crashed. Its neighbour kept answering and the worker didn't restart.

AI use: AI-assisted, I reviewed and tested it.

  • Tests pass
  • Docs updated

@thockin Tim Hockin (thockin) left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Send me a PR of 50 lines and I will find 50 bugs. Make it 5000 lines and I will find none.

@thockin
Tim Hockin (thockin) added this pull request to the merge queue Oct 29, 2026
Merged via the queue into agent-substrate:main with commit 89475d5 Oct 29, 2026
7 checks passed
@BenTheElder
Benjamin Elder (BenTheElder) deleted the microvm-oom-kill branch October 29, 2026 06:48
}
// the page cache the write adds isn't the actor running away
if ra != nil {
defer ra.memory.Resume(ra.memory.Pause())

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This re-arms the guard as soon as the write returns, while the snapshot's writeback pages are still charged to the leaf. Restoring memory.high doesn't reclaim them, so the overshoot check (memwatch.go:74) can kill the paused VM on the next tick, and a checkpoint that fails after the write gets reported as a memory kill. Could the guard stay paused through teardown, and on error re-arm only once that cache is gone (or have the overshoot check exclude file cache)?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Claude :-)
I'll follow up on this when I'm at a keyboard. Also looking for results from downstream scale / density testing.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, confirmed and fixing in #3476

func (s *AteWorkerService) leafMemoryHigh(memoryBytes int64) int64 {
if !s.throttleMemory || memoryBytes <= 0 {
return 0
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With memory.high at the booking, the 4MiB + 8MiB/GiB overhead becomes a kill threshold for a guest that touches all its RAM. Was a 100% fill tried? The test in the description stopped at 925MiB.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MicroVM guests can't directly touch ram outside, they would be triggering the interior kernel oomkiller as the VMM already configured a memory limit which is below the cost+ limit.

This is a backstop against some kind of leak on the outside of the VMM.

VMM leak aside, if they do a lot of file system reads then we fill page cache with virtiofsd.

shankgan pushed a commit to shankgan/substrate that referenced this pull request Oct 31, 2026
…strate#3476)

Follow-up to agent-substrate#3429, from @aramase's review:
agent-substrate#3429 (comment)

When the pod can lend a microVM snapshot write all the memory it needs,
the actor's cgroup has no `memory.high` during the write, so it can end
with a lot of dirty page cache. Putting `memory.high` back reclaims what
it can, but gives up on pages still dirty or under writeback:
https://github.com/torvalds/linux/blob/v7.0/mm/memcontrol.c#L4409-L4429

The memory guard resumes as soon as the write returns, before teardown,
and its overshoot check counted that page cache. So it could kill the
paused VM and report the actor crashed for exceeding its memory limit.

The overshoot check now leaves reclaimable page cache out too, and both
checks take what's left from one `memory.stat` read. The kernel only
flushes `memory.stat` past a threshold of updates, so it can trail
`memory.current`, and subtracting one from the other could count fresh
page cache as used:
https://github.com/torvalds/linux/blob/v7.0/mm/memcontrol.c#L561-L564

AI use: AI-assisted, I reviewed and tested it.

- [x] Tests pass
- [x] Docs updated

---------

Signed-off-by: Benjamin Elder <[email protected]>
Dmitry Berkovich (dberkov) pushed a commit that referenced this pull request Oct 31, 2026
…uard's overshoot check (#3513)

Cherry-pick of #3476 onto `release-1.0` (`git cherry-pick -x` of the
squash commit), it applied cleanly with no changes.

The memory guard's overshoot check leaves reclaimable page cache out, so
it no longer kills a paused microVM and reports the actor crashed when a
snapshot write leaves dirty page cache behind. This fixes a bug in
#3429, which is in 1.0.

AI use: AI-assisted. Clean pick.

- [x] Tests pass
- [x] Docs updated

Signed-off-by: Benjamin Elder <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/microVM breaking-change PRs with breaking changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants