chore(runway): correct what GOMEMLIMIT accounts for
Summary
The GOMEMLIMIT comment in .runway/values.yaml told an operator to budget
the gap below limits.memory for goroutine stacks and runtime metadata. The
ceiling already accounts for both, so the gap read larger than it is.
runtime/debug.SetMemoryLimit documents the limit as "all memory mapped,
managed, and not released by the Go runtime" and gives the quantity the runtime
maintains as MemStats.Sys - MemStats.HeapReleased. Sys covers the heap, the
stacks and the other runtime data structures, so StackSys and GCSys sit
inside the 400 MiB ceiling rather than in the 112 MiB gap beneath the 512Mi
limits.memory.
What changed
.runway/values.yaml now says what the ceiling covers, then what the gap
below it is for, which is two costs rather than one.
- What the ceiling excludes: the binary's own image and the OS kernel
memory held on behalf of the process. The doc's other exclusions are inert
here, so listing them would send an operator hunting a term that is always
zero. The image the workload pulls is built with
CGO_ENABLED=0(thebuildsenv block in.goreleaser.yml, whichdocs/dev/releases.mdnames under "The image is built on the tag pipeline" as the build that produces it). So there are no C allocations, and nothing in the build callssyscall.Mmap: no hit underinternal/orcmd/, and none in the dependency sourcesgo list -deps ./cmd/artifact-registryresolves either, where the only matches are test files ingolang.org/x/sys. - The ceiling's own overshoot:
GOMEMLIMITis soft. The collector cannot drive the heap goal below the live set, and the GC CPU limiter caps GC CPU at 50% (runtime/mgclimit.go) rather than collecting without bound, so past that point the footprint grows through the ceiling and the cgroup limit is what stops it. Naming only the exclusions would leave the gap reading smaller than it is, which is the same defect the other way round.
The replacement does not claim either figure is an RSS measurement, because
Sys is reserved address space rather than resident memory.
.runway/fairway.yaml changes in three places.
- It carried the same framing by reference, as "the non-heap RSS margin
.runway/values.yamldescribes", so it moves with the wording it points at. - It had two sentences saying these values must not move while the OOM work item stayed open. That work item closed on 2026-09-03, with the fix confirmed on staging, so as written the block now reads as a freeze with no end. It points at the work item that owns the sizing instead.
- Its sizing recipe said "under a push load" with no scale qualifier.
maxInstanceslets the HPA scale out, so a load split across replicas reads as a floor rather than a peak, and the one staging measurement that exists was taken that way. The recipe now says the load has to be sustained and taken by one replica.
What this does not do
The numbers are unchanged: requests.memory: 256Mi, limits.memory: 512Mi,
GOMEMLIMIT: "400MiB". Sizing them needs a sustained single-pod push load and
a Mimir query, which is the other half of the linked work item and does not
block this correction, so this MR does not close it.
Testing
Comments only, and outside the comment-caps gate: scripts/ci/check-comment-caps.sh
diffs with -- '*.go', so a YAML block is not in its scope. Both files
re-parse under yaml.safe_load, and GOMEMLIMIT reads back as 400MiB.
No other open merge request touches .runway/: re-checked at head
d451257f1 against the changed-file list of all 91 merge requests open at
that point, one of which is this one. So there is no merge order to state.
Related to #1137 (closed)