You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(telemetry): resolve the process cgroup path and honor ancestor limits [INFP-589]
The v2 limit files were read only at /sys/fs/cgroup, which is correct under
a private cgroup namespace (the modern Docker/K8s default, where the
apparent root is the container's cgroup) but wrong without one — older
runtimes on v2, cgroupns: host, systemd services with unit limits. The v2
root cgroup carries no cpu.max/memory.max, so a limited component reported
no CPU assignment and the whole host's memory as its capacity, breaking the
licensing audit in exactly those environments.
The reader now resolves its own cgroup from the 0:: line of
/proc/self/cgroup and consults every level up to the root, taking the most
restrictive limit; memory usage is read at the level holding the effective
limit, since an ancestor limit is shared with siblings. Resolution failures
fall back to the previous root-only read, and v1 keeps the root read
(runtimes bind-mount the container's own v1 controllers there). Limits
above a private namespace root remain invisible by construction.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: dev/specs/infp-589-resource-telemetry/research.md
+5-3Lines changed: 5 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -32,10 +32,12 @@ Phase 0 decisions. Each resolves an unknown surfaced while planning against the
32
32
-**Rationale**: adopting the existing `memory_*` names/semantics for the new components makes DB, server, and workers byte-for-byte comparable (the naming decision, D14), and matches how the DB JMX (`TotalMemorySize` / `FreeMemorySize`) already reports. Pete's "RAM used" is preserved as a derived value, consistent with the DB.
33
33
-**No `memory_assigned`**: memory has no enforced-limit field — the container memory limit surfaces as `memory_total` capacity. Only `processor_assigned` carries the FR-003 no-fallback-null rule.
## D5 — cgroup v2 primary, v1 fallback, `null` otherwise; resolve the process's own cgroup path
36
36
37
-
-**Decision**: Read cgroup v2 first (`/sys/fs/cgroup/cpu.max`, `memory.max`, `memory.current`); fall back to v1 (`/sys/fs/cgroup/cpu/cpu.cfs_quota_us` + `cpu.cfs_period_us`, `/sys/fs/cgroup/memory/memory.limit_in_bytes` + `memory.usage_in_bytes`); return `null` for the affected field where neither is readable (non-Linux dev, unusual mounts).
38
-
-**Rationale**: production runs containers (v2 on current hosts, v1 still common); developer machines are macOS (neither) and correctly report `null` for `assigned`. v1 `memory.limit_in_bytes` reports a sentinel near `INT64_MAX` when unlimited — treat values at/above a high threshold as unlimited → `null`.
37
+
-**Decision**: Read cgroup v2 first (`cpu.max`, `memory.max`, `memory.current`); fall back to v1 (`/sys/fs/cgroup/cpu/cpu.cfs_quota_us` + `cpu.cfs_period_us`, `/sys/fs/cgroup/memory/memory.limit_in_bytes` + `memory.usage_in_bytes`); return `null` for the affected field where neither is readable (non-Linux dev, unusual mounts).
38
+
-**Correction (review finding, post-implementation)**: the v2 files were initially read only at `/sys/fs/cgroup` itself. That is correct under a *private cgroup namespace* (the default on Docker 20.10+/modern K8s, where the apparent root *is* the container's cgroup) but wrong without one (older runtimes on v2, explicit `cgroupns: host`, bare-metal systemd services with unit limits): the v2 root cgroup carries no `cpu.max`/`memory.max` at all, so a limited component reported `processor_assigned = null` and — worse for an audit — the whole **host's** memory as `memory_total`. Fixed by resolving the process's own cgroup from the `0::<path>` line of `/proc/self/cgroup` and consulting **every level up to the root**, taking the most restrictive limit (a limit may be enforced on an ancestor slice); memory usage is read at the level holding the effective limit, since an ancestor limit is shared with siblings. Any resolution failure falls back to the previous root-only read.
39
+
-**Known limitation**: limits set *above* a private namespace root (e.g. a K8s pod-level limit when the container itself has none) are invisible from inside the namespace and cannot be self-reported — a fundamental property of self-observation, not an implementation gap.
40
+
-**Rationale**: production runs containers (v2 on current hosts, v1 still common); developer machines are macOS (neither) and correctly report `null` for `assigned`. v1 `memory.limit_in_bytes` reports a sentinel near `INT64_MAX` when unlimited — treat values at/above a high threshold as unlimited → `null`. v1 keeps the root-level read: container runtimes bind-mount the container's own v1 controller directories at `/sys/fs/cgroup/<controller>`, so the root read is already container-scoped there.
39
41
40
42
## D6 — Self-report through the existing heartbeat channel
0 commit comments