mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-05-11 17:54:44 +00:00
d05e468598
## Summary Implements **part 1** of #836 — `GOMEMLIMIT` support so the Go runtime self-throttles GC under cgroup memory pressure instead of getting SIGKILLed. (Parts 2 & 3 — bounded cold-load batching + README ops docs — land in follow-up PRs.) ## Behavior On startup `cmd/server/main.go` now calls `applyMemoryLimit(maxMemoryMB, envSet)`: | Condition | Action | Log | |---|---|---| | `GOMEMLIMIT` env set | Honor the runtime's parse, do nothing | `[memlimit] using GOMEMLIMIT from environment (...)` | | env unset, `packetStore.maxMemoryMB > 0` | `debug.SetMemoryLimit(maxMB * 1.5 MiB)` | `[memlimit] derived from packetStore.maxMemoryMB=512 → 768 MiB (1.5x headroom)` | | env unset, `maxMemoryMB == 0` | No-op | `[memlimit] no soft memory limit set ... recommend setting one to avoid container OOM-kill` | The 1.5x headroom covers Go's NextGC trigger at ~2× live heap (per #836 heap profile: 680 MB live → 1.38 GB NextGC). ## Tests (TDD red→green visible in commit history) - `TestApplyMemoryLimit_FromEnv` — env wins, function does not override - `TestApplyMemoryLimit_DerivedFromMaxMemoryMB` — verifies bytes computation + `debug.SetMemoryLimit` actually applied at runtime - `TestApplyMemoryLimit_None` — no env, no config → reports `"none"`, no side effect Red commit: `7de3c62` (assertion failures, builds clean) Green commit: `454516d` ## Config docs `config.example.json` `packetStore._comment_gomemlimit` documents env/derived/override behavior. ## Out of scope - Cold-load transient bounding (item 2 in #836) - README container-size table (item 3) - QA §1.1 rewrite Closes part 1 of #836. --------- Co-authored-by: corescope-bot <bot@corescope>
33 lines
1.2 KiB
Go
33 lines
1.2 KiB
Go
package main
|
|
|
|
import (
|
|
"runtime/debug"
|
|
)
|
|
|
|
// applyMemoryLimit configures Go's soft memory limit (GOMEMLIMIT).
|
|
//
|
|
// Behavior:
|
|
// - If envSet is true (GOMEMLIMIT env var present), the runtime has already
|
|
// parsed it; we leave it alone and report source="env" with limit=0.
|
|
// - Otherwise, if maxMemoryMB > 0, we derive a limit of maxMemoryMB * 1.5 MiB
|
|
// and set it via debug.SetMemoryLimit. This forces aggressive GC under
|
|
// cgroup pressure so the process self-throttles before SIGKILL. See #836.
|
|
// - Otherwise, no limit is applied; source="none".
|
|
//
|
|
// Returns the limit (in bytes) we actually set, or 0 if we did not set one,
|
|
// plus a short source identifier ("env" | "derived" | "none") for logging.
|
|
func applyMemoryLimit(maxMemoryMB int, envSet bool) (int64, string) {
|
|
if envSet {
|
|
return 0, "env"
|
|
}
|
|
if maxMemoryMB <= 0 {
|
|
return 0, "none"
|
|
}
|
|
// 1.5x headroom over the steady-state packet store budget covers
|
|
// transient peaks (cold-load row-scan / decode pipeline, Go's NextGC
|
|
// trigger at ~2x live heap). See issue #836 heap profile.
|
|
limit := int64(maxMemoryMB) * 1024 * 1024 * 3 / 2
|
|
debug.SetMemoryLimit(limit)
|
|
return limit, "derived"
|
|
}
|