Skip to content

telemetry: add full-pipeline stress tests#1951

Open
e-eygin wants to merge 3 commits into
ai-dynamo:mainfrom
e-eygin:nix-1205-telemetry-stress-tests
Open

telemetry: add full-pipeline stress tests#1951
e-eygin wants to merge 3 commits into
ai-dynamo:mainfrom
e-eygin:nix-1205-telemetry-stress-tests

Conversation

@e-eygin

@e-eygin e-eygin commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

What?

Add test-only, full-pipeline stress coverage for the telemetry system. No product, queue, exporter, schema, or public-API changes.

  • test/gtest/telemetry_stress_test.cpp (new telemetryStressTest suite): mixed concurrent value parity under capacity (each producer emits a distinct value stream; the drained per-type value multiset must equal the produced multiset — proves no value is corrupted, duplicated, or lost), addXferStats batch atomicity, repeated queue/periodic-task lifecycle shake-out, and safe shutdown with an in-flight periodic flush — all end-to-end through the BUFFER exporter.
  • test/gtest/telemetry_prometheus_stress_test.cpp: concurrent staging-overflow conservation (accepted*4 + dropped == produced, drops a multiple of 4) and concurrent mixed-workload aggregation (exact counter sums, valid last-op gauges, bounded error labels, zero drops under capacity). Reuses the shared prometheus_telemetry_fixture.h and the OpenMetrics/timeSeries scrape helpers.
  • test/doca-telemetry/telemetry_doca_stress_test.cpp (new standalone doca_nixl_stress_test binary): high-volume DOCA exporter counter/gauge/drop accumulation, isolated from the functional DOCA exporter tests.

Why?

Closes NIX-1205 (Extend telemetry tests) — https://linear.app/nvidia/issue/NIX-1205.
Existing tests cover queue mechanics and single-threaded exporter behavior directly, but not the concurrent, full observable pipeline (producers -> metric mapping/activation -> staging queue -> periodic flush -> exporter -> observable output). This suite pins down that contract — conservation, addXferStats all-or-none batching, per-value fidelity, and lifecycle/shutdown safety — so a regression anywhere in that path surfaces as a failing assertion. It is also the observable validation gate for the staging-queue work (NIX-1542 extraction, NIX-1544 low-lock): the same suite must pass unchanged across queue implementations. It builds only on already-merged behavior (NIX-454 drop counter #1887, NIX-453 activation #1919), so it is independent and mergeable on its own.

How?

  • Deterministic and timing-independent: assertions are conservation identities and value multisets, never a fixed accepted/dropped split; producer coordination uses a start latch; completion uses bounded polling with generous (sanitizer-aware) timeouts instead of sleeps.
  • Under-capacity tests size the BUFFER ring above the total event count so nothing is dropped; the overflow case drives the lossless, ring-free Prometheus exporter so accepted + dropped == produced is exact and hardware-independent.
  • Sanitizer builds scale iteration counts down at compile time (test-only) to stay CI-friendly and limit repeated thread-pool create/destroy.
  • Validation: normal C++17, TSan, UBSan, and DOCA-enabled builds all pass; the focused telemetry suites run warning-clean. (ASan cannot run in some environments due to a prebuilt mixed-Abseil ABI incompatibility, documented in the plan.)

Summary by CodeRabbit

  • Tests
    • Added high-volume stress coverage for telemetry exports, including concurrent Prometheus /metrics scraping and verification of counters, “last operation” gauges, and drop accounting.
    • Added concurrency-focused stress assertions for overflow conservation, multi-family aggregation, and correct error/status labeling behavior.
    • Added end-to-end stress coverage for concurrent pipelines: batching atomicity, repeated lifecycle construction/destruction, and safe shutdown during in-flight flushing.
    • Added a new standalone DOCA telemetry stress test target to the test suite with a dedicated timeout.

Add test-only stress coverage for the observable telemetry pipeline
(concurrent producers -> metric mapping/activation -> staging queue ->
periodic flush -> exporter -> observable output), complementing the direct
staging-queue unit tests. The suite is written to pass unchanged on both the
mutex-backed staging queue and a future low-lock implementation.

- test/gtest/telemetry_stress_test.cpp: mixed concurrent value parity under
  capacity (per-type sent-vs-received value multiset equality), addXferStats
  batch atomicity, repeated queue/periodic-task lifecycle shake-out, and safe
  shutdown with an in-flight periodic flush -- all end-to-end through BUFFER.
- test/gtest/telemetry_prometheus_stress_test.cpp: concurrent staging-overflow
  conservation and concurrent mixed-workload aggregation, sharing the fixture
  and scrape helpers extracted into telemetry_prometheus_test_util.h.
- test/doca-telemetry/telemetry_doca_stress_test.cpp: high-volume exporter
  counter/gauge/drop accumulation, as its own doca_nixl_stress_test binary.

No product, queue, exporter, schema, or public-API changes.

Signed-off-by: Efraim Eygin <eeygin@nvidia.com>
@e-eygin e-eygin requested a review from a team as a code owner July 15, 2026 14:19
@copy-pr-bot

copy-pr-bot Bot commented Jul 15, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@e-eygin e-eygin self-assigned this Jul 15, 2026
@github-actions

Copy link
Copy Markdown

👋 Hi e-eygin! Thank you for contributing to ai-dynamo/nixl.

Your PR reviewers will review your contribution then trigger the CI to test your changes.

🚀

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 32b79bae-340d-4b70-949b-50ba334b76aa

📥 Commits

Reviewing files that changed from the base of the PR and between 1344c93 and 9d85903.

📒 Files selected for processing (1)
  • test/gtest/telemetry_stress_test.cpp

📝 Walkthrough

Walkthrough

Adds high-volume and concurrent telemetry stress tests covering Prometheus aggregation, event-ring integrity, batch atomicity, lifecycle shutdown, and DOCA exporter counters. Meson builds and registers the new standalone DOCA stress executable and compiles the additional gtest sources.

Changes

Telemetry stress coverage

Layer / File(s) Summary
Prometheus aggregation and overflow tests
test/gtest/telemetry_prometheus_stress_test.cpp
Adds scraping, polling, overflow-conservation, concurrent aggregation, gauge, error-label, and drop-counter assertions.
Telemetry pipeline integrity tests
test/gtest/telemetry_stress_test.cpp, test/gtest/meson.build
Adds concurrent event parity, ring-content validation, batch atomicity, and gtest build integration.
Lifecycle and shutdown tests
test/gtest/telemetry_stress_test.cpp
Adds repeated lifecycle cycles and bounded destruction checks during in-flight periodic consumption.
DOCA exporter stress target
test/doca-telemetry/telemetry_doca_stress_test.cpp, test/doca-telemetry/meson.build
Adds a standalone high-volume DOCA exporter test for cumulative counters, last-operation gauges, drop totals, and Meson test registration.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested reviewers: guy-ealey-morag

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change as telemetry full-pipeline stress tests.
Description check ✅ Passed The PR description follows the required What/Why/How template and includes the key implementation details and motivation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/doca-telemetry/meson.build`:
- Around line 69-85: Review the doca_nixl_stress_test Meson test registration
and explicitly set a timeout that safely exceeds scrapeUntilValue’s 15-second
polling window plus setup and flush overhead, particularly for slower CI and
sanitizer builds.

In `@test/doca-telemetry/telemetry_doca_stress_test.cpp`:
- Line 82: Update the telemetry metric assertions in the stress test to use
telemetryMetricDescriptor.gaugeName instead of the hardcoded
"agent_tx_last_bytes" value, including the later assertion around lines 119–120.
Keep the existing assertion behavior while deriving the expected gauge name from
the descriptor.

In `@test/gtest/telemetry_prometheus_stress_test.cpp`:
- Around line 149-160: Update the producer setup in the telemetry stress test,
including the analogous section around the other producer case, to wait on a
shared latch or barrier before calling telemetry.addXferStats. Initialize the
gate before creating threads, have each producer await it, and release the gate
only after all producer threads have been created so both stress cases execute
concurrently.
- Around line 260-265: Update the convergence condition in the stress test
around metrics.latestValue to also read and compare both labeled error counters
against their expected totals before declaring convergence. Preserve the
existing checks for tx, rx, tx_req, and mem, ensuring the post-join scrape waits
until all six metric families are current.

In `@test/gtest/telemetry_stress_test.cpp`:
- Around line 392-460: Strengthen the atomicity assertions in the telemetry
stress test around the producer workload and RingTally validation: do not rely
only on equal aggregate counts with excess capacity. Configure a boundary where
partial batch insertion is observable, or retain event ordering/call identifiers
while draining the ring and verify each addXferStats call’s four records is
published as an indivisible batch.
- Around line 545-567: Update the telemetry stress test around nixlTelemetry
destruction so it deterministically waits for the periodic flush to enter before
calling telemetry.reset(), using a blocking test exporter or flush-entry hook.
Replace the in-process EXPECT_LT-only timing check with an external timeout
mechanism that still fails when reset() deadlocks, while preserving the
shutdown_budget assertion for prompt completion.
- Around line 69-74: Rename the C++ type aliases EventCounts and ValueHistograms
to lower-case _t-suffixed names such as event_counts_t and value_histograms_t,
and rename the related event type alias to event_type_t. Update every reference
in the telemetry stress test, including the additionally flagged locations,
without changing behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: fa4f1e9a-e13f-4157-addf-93f9ab22100d

📥 Commits

Reviewing files that changed from the base of the PR and between b392820 and b55caff.

📒 Files selected for processing (5)
  • test/doca-telemetry/meson.build
  • test/doca-telemetry/telemetry_doca_stress_test.cpp
  • test/gtest/meson.build
  • test/gtest/telemetry_prometheus_stress_test.cpp
  • test/gtest/telemetry_stress_test.cpp

Comment thread test/doca-telemetry/meson.build Outdated
Comment thread test/doca-telemetry/telemetry_doca_stress_test.cpp Outdated
Comment thread test/gtest/telemetry_prometheus_stress_test.cpp
Comment thread test/gtest/telemetry_prometheus_stress_test.cpp Outdated
Comment thread test/gtest/telemetry_stress_test.cpp Outdated
Comment thread test/gtest/telemetry_stress_test.cpp Outdated
Comment thread test/gtest/telemetry_stress_test.cpp Outdated
- Rename test type aliases to the repo's lower_case _t convention
  (event_counts_t, value_histograms_t, event_type_t).
- Prove addXferStats atomicity by identity + contiguity: each call carries a
  unique per-call id and the drained ring is walked to verify every call's four
  events form one intact, ordered [post, transfer, bytes, requests] group with a
  consistent id, rejecting a torn or interleaved batch (equal aggregate counts
  under capacity did not prove this).
- Bound the safe-shutdown destructor with an external timeout so a teardown
  deadlock fails as an assertion instead of hanging the test binary.
- Release the concurrent Prometheus producers through a shared start gate so the
  workload runs concurrently rather than largely serially.
- Wait for both labeled error counters before declaring convergence in the
  concurrent Prometheus aggregation test, avoiding a stale-scrape race.
- Derive the DOCA last-op gauge name from telemetryMetricDescriptor.gaugeName
  instead of hardcoding it.
- Give the doca_nixl_stress_test an explicit Meson timeout above its scrape-poll
  window.

Signed-off-by: Efraim Eygin <eeygin@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/gtest/telemetry_stress_test.cpp`:
- Around line 465-513: Validate every event type’s range before using
eventIndex() to access counts in the manual ring walk. Apply the same guarded
behavior to the initial type and the xfer, bytes, and reqs members: report an
ADD_FAILURE for out-of-range values, avoid indexing counts, and preserve the
existing batch validation flow for in-range events.
- Around line 630-647: Update the shutdown task around telemetry destruction to
capture ownership by moving the telemetry unique_ptr into the callable, rather
than capturing the outer telemetry variable by reference. Ensure the outer
pointer is moved-from before the worker starts, so timeout-and-detach cannot
leave the thread referencing stack state while preserving exception propagation
on successful completion.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: a1239c0b-2f9b-4dec-9bf3-397f0f7d795a

📥 Commits

Reviewing files that changed from the base of the PR and between b55caff and 1344c93.

📒 Files selected for processing (4)
  • test/doca-telemetry/meson.build
  • test/doca-telemetry/telemetry_doca_stress_test.cpp
  • test/gtest/telemetry_prometheus_stress_test.cpp
  • test/gtest/telemetry_stress_test.cpp

Comment thread test/gtest/telemetry_stress_test.cpp
Comment thread test/gtest/telemetry_stress_test.cpp Outdated
- Range-guard the batch-atomicity ring walk so corrupted ring content fails via
  ADD_FAILURE instead of indexing counts[] out of bounds (mirrors drainRing).
- Fix a dangling-reference/data-race in the safe-shutdown test: move the
  telemetry unique_ptr into the destroy-worker task so the object lives in the
  worker's closure, not on the stack; on a timeout the abandoned deleter thread
  no longer references a destroyed local.

Signed-off-by: Efraim Eygin <eeygin@nvidia.com>
@e-eygin

e-eygin commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

@ColinNV @ovidiusm please review

@e-eygin

e-eygin commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

/build

@e-eygin

e-eygin commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test 9d85903

@svc-nixl

Copy link
Copy Markdown
Collaborator

🤖 CI Triage Agentnixl-ci-dl-gpu · commit 8c72cddc

TL;DR: The Docker image build failed because the aarch64 CI node could not reach ports.ubuntu.com (IPv6 unreachable + IPv4 connection timeouts) during apt-get install gdb gdb-multiarch. This is a transient CI network/infrastructure issue, not a code defect — retry the build once connectivity is restored.

Full analysis

Summary: Stage "Compiling NIXL Docker Image for DL" (and the earlier build_helper attempts, which later passed on retry) failed at Dockerfile STEP 15/17 during apt-get install.

Root cause: apt-get update && apt-get install -y --no-install-recommends gdb gdb-multiarch could not download packages: every attempt to connect to ports.ubuntu.com:80 returned "Network is unreachable" for the IPv6 mirror addresses (2620:2d:4002:1::10a/b/c) and "connection timed out" for the IPv4 mirrors (91.189.91.102/103/104). apt exited with status 100 → podman build exit 100. This is an environmental network-egress problem on the aarch64 build agent, not caused by commit 8c72cdd (PR #1951). Note the log shows continuous activity right up to the failure (no hang), and the same job's build_helper_dl stages that failed at 15:19 (stages 124/125) succeeded on retry a few minutes later (stages 142/160), confirming the transience.

Implicated commit: None — infrastructure failure, unrelated to the PR commit [REDACTED:Hex High Entropy String].

File: .ci/dockerfiles/Dockerfile.gpu-test (the RUN sudo apt-get update && sudo apt-get install -y --no-install-recommends gdb gdb-multiarch && .gitlab/build.sh ... layer, STEP 15/17)

Suggested fix:

  • Primary: Re-run the build once the CI network can reach the Ubuntu ports mirror. No code change should be needed.
  • To harden against recurrence: point the aarch64 build at a reliable internal apt mirror/proxy (Artifactory already serves other content in this pipeline), and/or add retry+--fix-missing resilience to the apt step, e.g. apt-get -o Acquire::Retries=5 update and retry the install. Disabling IPv6 for apt (Acquire::ForceIPv4 "true") would also avoid the immediate "Network is unreachable" IPv6 attempts.

Related: none

🛡️ This comment had 1 potential secret(s) redacted (Hex High Entropy String). See request_id ee9635ab-9f45-41fc-8b75-151133afbf5e in the triage console for the audit trail.

@svc-nixl

Copy link
Copy Markdown
Collaborator

🤖 CI Triage Agentnixl-ci-dl-gpu-ep · commit 8c72cddc

TL;DR: The Docker image build (stages 150 & 125) failed because apt-get install gdb gdb-multiarch couldn't reach ports.ubuntu.com ("Network is unreachable" for all IPs), exiting 100 — this is a build-agent network/egress problem, not a code bug. Retry the build once network access to the Ubuntu ports mirror is restored.

Full analysis

Summary: podman build of Dockerfile.gpu-test failed at STEP 15/17 during apt-get install -y gdb gdb-multiarch with exit code 100.

Root cause: The aarch64 build agent could not connect to http://ports.ubuntu.com — every apt fetch returned connect (101: Network is unreachable) on both IPv6 (2620:2d:4002:1::10a/b/c) and IPv4 (91.189.91.102/103/104) addresses, so apt could not download the .deb packages and aborted (E: Unable to fetch some archives). The packages.microsoft.com HTTPS repo succeeded, so this is a transient egress/proxy/routing outage affecting only the plain-HTTP Ubuntu ports mirror, not the PR's code. Both failing stages (150 = ucx-master path, 125 = ucx-v1.22.x path) show the identical error, reinforcing that it's environmental.

Implicated commit: unknown — not caused by commit 8c72cdd; the Dockerfile apt step is pre-existing and the error is a network outage.

File: .ci/dockerfiles/Dockerfile.gpu-test:34-35

Suggested fix: Re-run the build once network connectivity to ports.ubuntu.com is restored. To harden against recurrence: (1) point the build agents at an internal/artifactory apt mirror instead of the public ports.ubuntu.com; (2) add apt retry/timeout options (e.g. -o Acquire::Retries=3 -o Acquire::ForceIPv4=true) since IPv6 was tried first and is unreachable; and/or (3) bake gdb/gdb-multiarch into the base image so PR builds don't hit the external mirror. Do not treat this as a source-code failure.

Related: none found.

@svc-nixl

Copy link
Copy Markdown
Collaborator

🤖 CI Triage Agentnixl-ci-gpu · commit 8c72cddc

TL;DR: The GPU CI node's UCX library was loaded without CUDA support (cuDeviceGetCount ... failed: initialization error), so HardwareWarningTest.NoWarningWhenIbAndCudaSupported failed hard ("Failed to create UCX context: No such device") and ~68 other UCX tests tripped the "unexpected NIXL warning" harness (exit 42). Fix the container/UCX so CUDA (and IB) transports are actually available on the test node — this is an environment regression, not a test-code bug.

Full analysis

Summary: The "Run CPP tests" stage (node 203) failed with exit code 42; the sole genuine test failure was HardwareWarningTest.NoWarningWhenIbAndCudaSupported, and 68 UCX tests were flagged for an unexpected hardware-support warning.

Root cause: On node mizu04, UCX could not initialize CUDA: cuda_ctx.c:52 UCX ERROR cuDeviceGetCount(&num_devices) failed: initialization error, and ucp_context.c: transports 'ib','cuda' are not available. With 8 GPUs and 4 IB devices physically present but no CUDA/RDMA transport in UCX, nixlUcxContext::warnAboutHardwareSupportMismatch() emits "8 NVIDIA GPU(s) were detected, but UCX CUDA support was not found!" on nearly every test (harness treats any NIXL warning as failure → exit 42). The one test that requires ib,cuda transports, NoWarningWhenIbAndCudaSupported, threw "Failed to create UCX context: No such device" (real FAILED, exit 1). This is a broken UCX build/runtime in the image, not a code defect — consistent with the concurrent build_helper failures for ucx-master (stage 124) and ucx-v1.22.x (stage 125).

Implicated commit: unknown — not a source regression; environment/UCX-image issue (related to the in-flight UCX version bump work).

File: src/plugins/ucx/ucx_utils.cpp:639 (warning site) / test HardwareWarningTest.NoWarningWhenIbAndCudaSupported in the ucx gtest suite.

Suggested fix: Rebuild/repair the CI container so UCX is compiled and linked with working CUDA (and IB/RDMA) support, and verify the CUDA driver is accessible in the enroot/pyxis container on mizu04 (the cuDeviceGetCount ... initialization error indicates the CUDA driver//dev/nvidia* is not visible to the process). Confirm ucx_info -d shows cuda and ib transports before running the CPP suite. As a secondary hardening step, NoWarningWhenIbAndCudaSupported should skip (GTEST_SKIP) rather than throw when the runtime lacks CUDA/IB transports.

Related: PR #1868 (bump UCX to v1.22.x), PR #1743 (gtest single-process runner); build_helper stages 124/125 (ucx-master, ucx-v1.22.x) failed in this same build.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants