fix(ingest): convert tachometer parquet that lacks timestamp_ns - #414
Conversation
Every published tachometer-scraper release asset up to v1.0.96 predates the vendored writer's timestamp_ns column (NVIDIA#350, 2026-08-26): the release workflow's build-tachometer-scraper jobs have failed at actions/checkout since the NVIDIA#350 merge (runs 33043846919, 34112274235), and every release since re-uses the previous release's binaries. Such scrapers write only time_since_start, and the ingest died on every capture with KeyError: 'Field "timestamp_ns" does not exist in schema' (seen on hecate 487539 and again on 565810), so the metrics leg of the perf dashboard bundle was silently empty for runs that had the data on disk. When the column is absent, derive it: epoch of time_since_start == 0 from an explicit --start-ns, else the first timestamped line of the run's tachometer.out (the scraper logs within the second it starts counting from), else the newest parquet's mtime minus its largest time_since_start (row-group statistics). The source is logged at WARNING; precision equals the one-second grid every timestamp is snapped to. Parquet with the real column is unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yuewei Na <nv-yna@users.noreply.github.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #414 +/- ##
=======================================
Coverage ? 75.05%
=======================================
Files ? 109
Lines ? 16002
Branches ? 0
=======================================
Hits ? 12011
Misses ? 3991
Partials ? 0 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Evidence from the three hecate runs (2026-09-09, AgentX baseline recipe, 8 VR200 nodes)All three postprocessed with this branch's 565810 — release-asset scraper (v1.0.96 binary, no Without this change the same step raised 565811 and 565854 — scraper rebuilt from source (has ( Two adjacent observations, not fixed here: the scraper never writes |
Nine conflicting files, resolved by hand: - The process exporter (#413) and the wider node-exporter collector set (#415) arrive as tachometer-stage exporter launches; on this branch the exporters are services. process-exporter is now a service kind (`type: process-exporter`, implied while tachometer runs, `placement.node: all`), host-native from the `configs/process-exporter` binary `make setup` installs (no container, no mounts), skipped with a warning when the binary is missing, container launch when a recipe declares a `container`. The group file is written by the kind's `prepare` hook; `ServiceKind` gains `host_native`, `prepare`, and `skip_reason`. node-exporter's built command carries the stat/vmstat/pressure/meminfo_numa/processes collectors and the widened vmstat field filter. The templates, the group YAML, and the host binary resolver move to `srtctl.services.exporters`; their tests follow. - Load-window tachometer (#359): start/stop inside run_benchmark is kept; `stop_tachometer` now terminates through `ManagedProcess.terminate`, which signals the Slurm step (SIGTERM to the srun client would abort the step and SIGKILL the scraper). The scraper's `terminate_timeout` is the recipe's `shutdown_grace_secs`; the 90 s module constant is gone. - Ingest timestamp fallback (#414) is taken from main wholesale (it also offers `--start-ns`); this branch's own fallback is dropped, its dedup module and docstring edits re-applied. - CPU power telemetry (#410, #422): taken as is (power is frozen); the energy report runs before the S3 upload, which returns the URL only. - Makefile: the `examples`/`golden-check` targets plus the cpu-power targets; the `recipes/`-based runner targets stay deleted. SUMMARY: both new pages, `analyzing.md` stays deleted. submit.py: the direct-host renderer import stays deleted; the arch helpers the cpu-power preflight uses are kept. telemetry.py: IPv6-safe host and the SGLang gateway metrics port together. 2355 tests on Python 3.10 and 3.13, lint, schema docs, 21 examples validated, golden 574 identical / 0 mismatched.
What
src/ingest/metrics_tachometer.pynow converts tachometer parquet that has notimestamp_nscolumn. When the column is absent, the epoch oftime_since_start == 0is taken from, in order:--start-ns(new CLI flag;process(..., start_ns=)),tachometer.out(searched up to four levels above the parquet; the scraper logs within the second it starts counting from),time_since_start(row-group statistics, no data read).The source used is logged at WARNING. Precision equals the one-second grid every timestamp is snapped to anyway. Parquet that carries the real column is processed exactly as before.
Why
Every published
tachometer-scraperrelease asset up to v1.0.96 predates the vendored writer'stimestamp_nscolumn (#350, merged 2026-08-26). The release workflow'sbuild-tachometer-scraperjobs have failed atactions/checkout@v4on every merge that touched the Rust sources since then (runs 33043846919 for #350 and 34112274235 for #401, both jobs failing within 4 s), thereleasejob was skipped, and every later release took the "reuse scraper binaries from the previous release" path. Result: the v1.0.96 aarch64 asset is byte-identical (sha256344181ae…) to a download from 2026-08-27, andmake setupinstalls it.Such scrapers write only
time_since_start, and the ingest died on every capture:Seen on hecate job 487539 (2026-08-27) and again on 565810 (2026-09-09). The metrics leg of the perf dashboard bundle was therefore silently empty for runs that had the full capture on disk.
The release workflow itself needs a separate fix (the
pull_request_targetcheckout ofmerge_commit_sha); until the assets are refreshed,make tachometer-scraper(or the Dockerfile cross-build) produces a correct binary. This change makes the ingest independent of which binary a run happened to use.Why draft
Held until the three hecate runs that exercise it finish and post-process end to end: 565810 (old binary, fallback path) and 565811 / 565854 (rebuilt binary,
timestamp_nspresent). Will attach the ingest log lines from each before marking ready for review.Validation
tests/test_metrics_tachometer.py: 4 new tests (log-anchored, mtime-anchored, explicit--start-ns, and real-column-preferred-over-stale-log) + 8 existing → 12 passed;pytest -k "tachometer or ingest or metrics"→ 102 passed.2026-09-10T00:37:10Z..00:54:02Z, anchored on the scraper's first log stamp00:37:08Z; 125 families in the first line. ~2.5 min on the login node.ruff checkon the two files reports the same 5 findings main already has (RUF100, SIM108, SIM115, RUF046 on the pre-existing snap line); this diff adds none.🤖 Generated with Claude Code