Skip to content

obs: per-sandbox resource usage, claim-tier histograms, trace propagation #37

Description

@CMGS

Summary

Two observability gaps: nothing measures what a sandbox actually consumes (CPU seconds, RSS) — so no noisy-neighbor detection, no usage-based billing beyond wall-clock seconds, no overcommit data — and claim latency is exported only as counter totals (averages), hiding tails. Three proposals, independently landable.

Current state

  • /metrics (hand-rendered, server/metrics.go): pool/claim gauges, lifecycle counters, *_seconds_total for average latency only.
  • The usage journal bills wall-clock seconds by lifecycle interval folding; it never sees actual resource consumption.
  • Per-claim VMM pids are known to cocoon; nothing reads their /proc stats.
  • No trace correlation between an SDK call and the node's logs/journal.

Proposal

1. Per-sandbox resource stats

Source: the VMM process (/proc/<pid>: utime+stime, VmRSS) — works for CH and FC alike, no guest cooperation, no cgroup dependency.

  • GET /v1/sandboxes/{id}/stats (sandbox token) and GET /v1/stats (root, all claims) — sampled on demand, {cpu_seconds, rss_bytes, state}.
  • Per-tenant aggregates on /metrics (sandboxd_tenant_cpu_seconds_total, sandboxd_tenant_rss_bytes), sampled by the existing telemetry tick. Deliberately NO per-sandbox Prometheus series — unbounded cardinality; the operator index + stats endpoint covers spot checks.
  • Billing: release/reap/hibernate usage events gain cpu_seconds/max_rss — read once at teardown, off the claim path. Cumulative across hibernate cycles: accumulate into the claim record at each hibernate (the VMM dies there; the counter must survive it). A reap whose VMM is already gone records the last accumulated value — honest, not fabricated.

2. Claim-path histograms

Replace averages-only with fixed-bucket histograms per tier (warm/clone/cold) and per phase (queue → provision → probe), hand-rendered like the rest of metrics.go. Buckets chosen from the numbers of record (0.5ms/5/50/100/250/500/1000). The phase timestamps largely exist for the *_seconds_total counters; this changes the export shape, not the instrumentation.

3. Trace propagation (cheap, no OTel dependency)

Accept traceparent on the resource-creating endpoints; echo the trace id into the structured claim log line and the usage event. Correlation happens in the operator's collector. Full OTLP export is rejected for now: an SDK dependency + config surface for something a log pipeline already does; revisit when someone actually runs a tracing backend against this.

Hot-path cost

Claim path: zero new syscalls (phase timestamps already taken; histogram observe is arithmetic). Stats: on-demand reads + one /proc read per claim per telemetry tick for tenant aggregates (bounded by live claims, off the data plane). Teardown: one /proc read before destroy. State the measured delta in the PR per the perf discipline.

Acceptance evidence

  • Unit: stat parsing, hibernate-cycle accumulation, reap-after-death fallback.
  • e2e: spin CPU in a sandbox → stats shows monotonic cpu_seconds; release event carries plausible cpu_seconds; /metrics shows tenant gauges and tier histograms; rpcbench p50/p99 unchanged vs baseline in the same run.

Estimated size: ~200-250 lines + tests; proposals land separately (1 is the substance, 2 and 3 are small).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions