Repository navigation
feat(infra): show pod restarts and last termination reason - #1389
Conversation
Adds podRestartsQuery over the cluster receiver's k8s.container.restarts (window restarts = max - min of the running total per container) plus the k8s.container.status.last_terminated_reason resource attribute, a podRestarts endpoint, an inline restart marker on the pod list, and a restarts tile and per-container table on the pod page.
Maple review🟢 Confidence 7/10 · likely safe to merge Adds a pod-restarts query, endpoint and UI: window restarts, the lifetime total and the last termination reason per pod. The restarts aggregate overcounts when the kubelet resets the counter inside the window; the open
Findings🟠 Warning · F1 ·
|
| Issue | Service | Occurrences | File |
|---|---|---|---|
| @maple/web/errors/WarehouseInvalidInputError | maple-web | 12 | apps/web/src/lib/services/atoms/warehouse-query-atoms.ts |
After this merges, Maple checks whether they stop.
Production traffic of the changed files (last 7 days)
| File | Calls/day | Busiest operations |
|---|---|---|
apps/api/src/routes/internal/query-engine.http.ts |
5 | podInfraTimeseries 5/day, 0.0% err |
packages/domain/src/http/query-engine.ts |
5 | podInfraTimeseries 5/day, 0.0% err |
What was checked
- Org scoping kept:
OrgIdfilter in the query (infra.ts:715) andCurrentTenant.Contextin the handler (query-engine.http.ts:1556) - Pod names go through
CH.inListand an empty page drops the clause instead of emittingIN () - New endpoint rides the group's
HttpMiddleware.tracerinstrumentation (apps/api/src/http/api-observability.ts:28)
Observability coverage: 2 of 2 changes observable
| Change | Kind | Observable | Evidence |
|---|---|---|---|
| POST /internal/query-engine/pod-restarts | server | yes | HttpApiBuilder group covered by ApiObservabilityLive's HttpMiddleware.tracer, apps/api/src/http/api-observability.ts:28-35 |
| podRestartsQuery (ClickHouse) | client | yes | runs through the shared WarehouseQueryService.compiledQuery path like every registry query (query-engine.http.ts:1559) |
f8a0e63 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configuration
📒 Files selected for processing (19)
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Maple review🟢 Confidence 7/10 · likely safe to merge Adds a
Still open from earlier reviews
Production impactOpen errors in the changed files
After this merges, Maple checks whether they stop. Production traffic of the changed files (last 7 days)
What was checked
Observability coverage: 2 of 2 changes observable
|
Maple review🟢 Confidence 8/10 · likely safe to merge Adds a
Findings🔵 Note · F2 ·
|
| Issue | Service | Occurrences | File |
|---|---|---|---|
| @maple/web/errors/WarehouseInvalidInputError | maple-web | 12 | apps/web/src/lib/services/atoms/warehouse-query-atoms.ts |
After this merges, Maple checks whether they stop.
Production traffic of the changed files (last 7 days)
| File | Calls/day | Busiest operations |
|---|---|---|
apps/api/src/routes/internal/query-engine.http.ts |
5 | podInfraTimeseries 5/day, 0.0% err |
packages/domain/src/http/query-engine.ts |
5 | podInfraTimeseries 5/day, 0.0% err |
What was checked
greatest(argMax(Value, TimeUnix) - argMin(Value, TimeUnix), 0)is last − first, so the F1 overcount on a counter reset is gone (infra.ts:766)- Empty
podNamesdrops the name filter instead of emittingIN ()(infra.ts:783); the list only mounts the query with a non-empty page (pods/index.tsx:410) - The new endpoint inherits the group's auto server span (
HttpMiddleware.tracer, apps/api/src/http/api-observability.ts:31) and the atom family opens the query span
Observability coverage: 2 of 2 changes observable
| Change | Kind | Observable | Evidence |
|---|---|---|---|
| POST /pod-restarts (apps/api internal query-engine API) | server | yes | HttpApiBuilder group middleware HttpMiddleware.tracer stamps the server span (apps/api/src/http/api-observability.ts:31) |
| podRestarts ClickHouse query via the web atom client | client | yes | makeQueryAtomFamily runs the query on the traced atom runtime and it executes through compiledQuery with context "podRestarts" (warehouse-query-atoms.ts:242-260) |
46015d9 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.
…verviewGrain, MCP playground entries)
Maple review🟢 Confidence 8/10 · likely safe to merge Adds pod restart counts and the last termination reason: a
Fixed since the last review
Production impactOpen errors in the changed files
After this merges, Maple checks whether they stop. Production traffic of the changed files (last 7 days)
What was checked
Observability coverage: 2 of 2 changes observable
|
…reasons # Conflicts: # apps/web/src/components/infra/constants.ts # knip.json
Maple review🟢 Confidence 8/10 · likely safe to merge Adds
Production impactOpen errors in the changed files
After this merges, Maple checks whether they stop. Production traffic of the changed files (last 7 days)
What was checked
Observability coverage: 2 of 2 changes observable
|
Problem
The pod pages show memory against limit, but not restarts or why a container last terminated. The only way to spot an OOM kill was a memory curve that drops after a peak.
What the receiver emits (pinned contrib v0.151.0)
k8s.container.restarts: int gauge, the kubelet'sRestartCountper container. Resource attrs includek8s.pod.name,k8s.namespace.name,k8s.container.nameand the workload names. On by default.k8s.container.status.last_terminated_reason: a resource attribute on everyk8s.container.*metric, set fromLastTerminationState.Terminated.Reason(OOMKilled, Error, Completed, ...). Off upstream, but already enabled invalues.yaml(presets.clusterMetrics.resourceAttributes), so no chart behaviour change was needed. I only added a comment so it doesn't get removed as unused.k8s.container.status.reason/k8s.container.status.state: optional sum metrics for the current waiting/terminated reason (for example CrashLoopBackOff). I left these off. Each one emits 9 or 3 series per container on every interval, and the last-terminated reason already answers "why did it restart".k8s_events(thek8sobjectsreceiver, off by default): each event lands as a log. The body is the whole Event object. The attributes areevent.domain=k8s,event.name=<event object name>andk8s.resource.name=events, and the only resource attribute isk8s.namespace.name. The pod is only inbody.object.regarding.name, so a join means parsing JSON bodies. Also,OOMKillingis a node-problem-detector event on the Node rather than the Pod. Not used here.Changes
podRestartsQueryinch/queries/infra.ts. One row per (pod, namespace, container):restarts(last sample minus first in the window, floored at 0),totalRestarts(latest sample), andlastTerminatedReason(argMaxby time). It filters by OrgId and window, with optional page pod names, namespace and workload. The MCP infra tools landed their own copy on main while this was open; the merge keeps this one, which takes the same opts and returns the same fields pluslastTerminatedReason, and adds that field to the MCP test fixtures. There are benchmark builder entries for the workload and page shapes.POST /pod-restarts(PodRestartsRequest/PodRestartsResponse) plus a registry query and an API handler.3 restarts · OOMKilled. It's red for OOMKilled and amber for other reasons. The full total is in the hover title.Caveat: kubelet can reset
RestartCountwhen it prunes dead containers. Taking last minus first (rather than max minus min) means a reset inside the window can undercount but never overcounts.Follow-up
Follow-up: the MCP
inspect_infrapod view already callspodRestartsQueryand should now renderlastTerminatedReasonfrompodRestartsQuery, so an agent can name an OOM kill rather than infer it from memory.Tests
packages/query-engine:infra.test.ts(newpodRestartsQuerycases) andbenchmark/catalog.test.tsapps/web:components/infra/pod-restarts.test.ts(per-pod folding, reason choice, namespace separation, tone)Not verified against a live cluster or in the browser.
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Summary by CodeRabbit