Skip to content

memory guard: parking and session save/restore check the container's real RAM (--memory-limit-mib) - #1601

Closed
noon-at-cgn wants to merge 2 commits into
Niko1221:mainfrom
noon-at-cgn:pr/memory-guard
Closed

noon-at-cgn wants to merge 2 commits into
Niko1221:mainfrom
noon-at-cgn:pr/memory-guard

Conversation

@noon-at-cgn

@noon-at-cgn noon-at-cgn commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Title

Issue: Related: #1250 (host OOM while page-locking the expert copy; its STRATA_PIN_GUARD covers the start-up copy), #1572 / #1573 (SAVE admission, open draft).

Summary

Why it matters: parking a conversation (about 240 MB to 750 MB per park) and saving or restoring a session file decide with MemAvailable alone. Inside a container that figure ignores the container's own limit, so the engine can start a copy it has no room for. In our log from a bad day, MemAvailable read 21.8, 25.4 and 3.7 GiB just before three kills while the container's cgroup was at 98.6, 94.9 and 96.6 GiB of a ~100 GiB limit. available_host_bytes() now returns the smallest of MemAvailable, the room under every cgroup limit above the engine, and an operator cap (--memory-limit-mib N, the total RAM the container may use). With no cgroup limit and no flag it is MemAvailable, as before. Those kills were host-wide OOMs (the host was over-committed), so the PR does not claim to prevent them; it makes the engine's own admission decisions use the real headroom.

90 GiB container, resident RAM mode --memory-limit-mib 101376 --memory-limit-mib 78000
parking attempts that parked every one (135 parks, 0 skips in one 308-request run) 0 of 8
log line parked N tokens in ... ms skip parking (... 0 MiB available, source flag)
requests served normal normal (decode 79.6 tok/s)

The engine held 89.7-89.9 GiB, above a 76.2 GiB cap, so headroom is 0: the intended refusal, and the request goes on.

What changed

  • conversation_memory.hpp/.cpp: conversation_available_memory() becomes available_host_bytes() / sample_host_memory(). The cgroup term reads memory.max and memory.high (v2) or memory.limit_in_bytes (v1) of the process's group and each visible ancestor; clean inactive file cache is credited. An unparsable cgroup file makes the sample unknown, and unknown refuses.
  • src/program/generate.cpp: flag and variable, one start-up line (limit, source, usage, credit); parking (before and after capture), save and restore read the new figure; a refusal says how much was available and from which source. The eviction loop is unchanged.
  • sycl/src/program/generate.cpp: four callers renamed. tests/core/memory_guard_test.cpp (new, CPU, fixture directories for /proc/meminfo and the cgroup tree) + CMakeLists.txt; docs/DETAILS.md.

Extra Notes

Measured on engine 0.1.40.3 (d5ea713) plus this change; the branch itself is cut from main fb58e0d.
Relation to #1250: not duplicated, not touched. STRATA_PIN_GUARD decides at start whether to page-lock in steps; this covers parking, SAVE and RESTORE after the start. Both read cgroup limits, in separate code (conversation_memory.cpp also builds standalone for the conversation tests); a shared reader can follow if you prefer one. #1573 edits the same SAVE lines, so one of the two needs a rebase.
Machine: 2x RTX 3080, 90 GiB container, UD-Q4_K_XL. Checked: memory_guard_test (130 checks; an existing binary, the rebuild was blocked by the build lock) and conversation_memory_test, CUDA build. Not built: HIP, SYCL, Windows. Not measured: a cap between 78000 and 101376, any run without the flag, any throughput effect.
Context: this change is part of the setup measured in the community benchmark #1640.

…e really has (--memory-limit-mib)

available_host_bytes() = min(MemAvailable, the room under every cgroup limit above the engine, the operator's cap).
Parking and session save/restore read MemAvailable alone: in a container whose real limit sits on a parent cgroup it
cannot see, MemAvailable is stale (about 25 GiB shown when about 1 GiB was left) and the engine was OOM-killed.

conversation_available_memory() is replaced by available_host_bytes() / sample_host_memory() (the SYCL port's
generate.cpp too). The cgroup term covers memory.max and memory.high (v2) and memory.limit_in_bytes (v1) of the
process's group and every visible ancestor, with clean inactive file cache credited; an unparsable cgroup file makes the
sample unknown (fail closed). --memory-limit-mib / STRATA_MEMORY_LIMIT_MIB names the total RAM the container or cgroup
may use. Refusals say how much was available and from which source; one start-up line reports the limit.
tests/core/memory_guard_test.cpp runs on fixture directories (CPU only). Docs: DETAILS.md.
@noon-at-cgn noon-at-cgn changed the title Memory guard: parking and session save/restore check the RAM the container really has (--memory-limit-mib) memory guard: parking and session save/restore check the container's real RAM (--memory-limit-mib) Oct 9, 2026
@Niko1221

Copy link
Copy Markdown
Owner

Ported into v0.1.42 with your commits (one conflict with --conversation-cache-min-tokens and the renamed RAM probe of #1573, resolved by keeping both). Tested under a systemd cgroup with MemoryMax on an RTX 3060 box: the start-up line reports the cgroup as the source (limit 41.0 GiB while MemAvailable said 44 GiB), and a SAVE that /proc/meminfo alone would have admitted is refused with 'source cgroup'. Thank you. One change from the PR: when the cgroup files cannot be read or parsed and no --memory-limit-mib is given, the guard falls back to MemAvailable alone (as 0.1.41) with one warning, instead of refusing parking and SAVE; with the flag set it still refuses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants