Skip to content

Commit 034f5dd

Browse files
MgluhovskoiSimengLiu-nv
authored andcommitted
[None][fix] Cherry-pick #20777 from release/1.3: Validate telemetry deployment IDs as UUIDs
Forward-port of release/1.3 PR #20777 (release/1.3 commit 6c172f3) to main. Adapted to main: the new payload test patches _HEARTBEAT_STOP, which main renamed from _REPORTER_STOP. Signed-off-by: Maxim Gluhovskoi <mgluhovskoi@nvidia.com> Signed-off-by: Simeng Liu <simengl@nvidia.com>
1 parent ab6d70e commit 034f5dd

3 files changed

Lines changed: 72 additions & 30 deletions

File tree

‎tensorrt_llm/usage/schemas/README.md‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ exclude these sentinel values from parallelism averages and distributions.
109109
| `llmApiConfigJson` | string | JSON-serialized sanitized, type-driven effective LLM API configuration. See [LLM API config capture](#llm-api-config-capture). | `'{"tensor_parallel_size":2,...}'` |
110110
| `llmApiConfigMetaJson` | string | JSON-serialized metadata for LLM API configuration capture. | `'{"capture_succeeded":true,...}'` |
111111
| `disaggRole` | ShortString | Disaggregated serving role. Empty if not disaggregated. | `""`, `"context"`, `"generation"`, `"coordinator"`, `"server_coordinator"`, `"ctx0"`, `"gen0"` |
112-
| `deploymentId` | ShortString | Shared ID across disaggregated workers. Empty if not disaggregated. | `""`, `"dep-abc123"` |
112+
| `deploymentId` | ShortString | Shared disaggregated deployment UUID, normalized to lowercase hex. Empty if unset or invalid. | `""`, `"7cd46f928e324879b82a7cabd3c0ecde"` |
113113

114114
##### Architecture privacy policy
115115

@@ -163,7 +163,7 @@ exit, a terminal report, or telemetry opt-out, with no heartbeat-count limit.
163163
| `seq` | PositiveInt | Zero-based heartbeat sequence number, saturating at uint32 max. | `0`, `1`, `42` |
164164
| `ingressPoint` | ShortString | Invocation boundary for the process session. | `"cli_serve"` |
165165
| `disaggRole` | ShortString | Disaggregated role: `context`, `generation`, `coordinator`, `server_coordinator`, or compatible legacy values such as `ctx0`/`gen0`. | `"context"` |
166-
| `deploymentId` | ShortString | Optional shared disaggregated deployment ID. | `"dep-abc123"` |
166+
| `deploymentId` | ShortString | Optional shared disaggregated deployment UUID. | `"7cd46f928e324879b82a7cabd3c0ecde"` |
167167

168168
Every heartbeat also contains the five aggregate LLM lifecycle counters above.
169169

@@ -183,7 +183,7 @@ outcome. Missing terminal events remain unknown; they are not confirmed crashes.
183183
| `reportingSource` | enum | `self`, `supervisor`, or `executor_proxy`. |
184184
| `ingressPoint` | ShortString | Entry point copied onto the terminal event so terminal-only early failures remain attributable. |
185185
| `disaggRole` | ShortString | Disaggregated role (`context`, `generation`, `coordinator`, `server_coordinator`, or compatible legacy `ctx0`/`gen0`), or empty when unavailable/not applicable. |
186-
| `deploymentId` | ShortString | Optional shared disaggregated deployment ID. |
186+
| `deploymentId` | ShortString | Optional shared disaggregated deployment UUID. |
187187

188188
Every terminal report also contains the five aggregate LLM lifecycle counters
189189
above. Delivery is best-effort and waits no more than 0.5 seconds; the local
@@ -310,7 +310,13 @@ over time.
310310
| `TRTLLM_USAGE_HEARTBEAT_INTERVAL` | `600` | Heartbeat interval in seconds. |
311311
| `TRTLLM_USAGE_FORCE_ENABLED` | `0` | Set to `1` to force-enable telemetry in CI/test environments. |
312312
| `TRTLLM_DISAGG_ROLE` | unset | Disaggregated serving role (`context`, `generation`, `coordinator`, `server_coordinator`, or compatible legacy values such as `ctx0`/`gen0`). |
313-
| `TRTLLM_DISAGG_DEPLOYMENT_ID` | unset | Shared deployment ID across disaggregated workers. |
313+
| `TRTLLM_DISAGG_DEPLOYMENT_ID` | unset | Shared deployment UUID across disaggregated workers. Invalid values are reported as an empty ID. |
314+
315+
The disaggregated launcher generates a random deployment UUID and shares it
316+
through the environment. Custom launchers can also supply a shared UUID via
317+
`TRTLLM_DISAGG_DEPLOYMENT_ID`. Telemetry validates it with `uuid.UUID` and emits
318+
32 lowercase hexadecimal characters, or an empty string if invalid. This checks
319+
format, not who generated the UUID or whether it contains encoded identifying data.
314320

315321
## For Developers: Adding a New Field
316322

‎tensorrt_llm/usage/usage_lib.py‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@
8585
_T = TypeVar("_T")
8686

8787

88+
def _get_disagg_deployment_id() -> str:
89+
"""Return the environment's deployment UUID as lowercase hex, or empty if invalid."""
90+
try:
91+
return uuid.UUID(os.environ.get(_DISAGG_DEPLOYMENT_ID_ENV, "")).hex
92+
except ValueError:
93+
return ""
94+
95+
8896
@dataclass(frozen=True)
8997
class TerminalOutcome:
9098
"""One typed process-boundary or previously observed terminal outcome."""
@@ -794,7 +802,7 @@ def _refresh_metadata_unlocked(self) -> None:
794802
if not self.disagg_role:
795803
self.disagg_role = os.environ.get(_DISAGG_ROLE_ENV, "")
796804
if not self.deployment_id:
797-
self.deployment_id = os.environ.get(_DISAGG_DEPLOYMENT_ID_ENV, "")
805+
self.deployment_id = _get_disagg_deployment_id()
798806

799807
normalized_role = self.disagg_role.lower()
800808
if self.component == "server" and (
@@ -1071,9 +1079,7 @@ def _empty_event_snapshot(usage_context: str = "") -> dict[str, Any]:
10711079
return {
10721080
"ingressPoint": _clamp_str(usage_context, schema._SHORT_STR),
10731081
"disaggRole": _clamp_str(os.environ.get(_DISAGG_ROLE_ENV, ""), schema._SHORT_STR),
1074-
"deploymentId": _clamp_str(
1075-
os.environ.get(_DISAGG_DEPLOYMENT_ID_ENV, ""), schema._SHORT_STR
1076-
),
1082+
"deploymentId": _get_disagg_deployment_id(),
10771083
"llmInitializationAttempts": 0,
10781084
"llmInstancesCreated": 0,
10791085
"activeLlmInstances": 0,

‎tests/unittest/usage/test_reporter.py‎

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -595,31 +595,54 @@ def fake_send(payload):
595595

596596

597597
class TestDisaggMetadata:
598-
"""Verify _background_reporter() reads disagg env vars into initial report."""
598+
"""Verify disagg correlation accepts UUIDs but excludes arbitrary identifying text."""
599599

600-
def test_disagg_env_vars_appear_in_payload(self, monkeypatch, reporter_session):
601-
"""Disagg env vars appear as disaggRole and deploymentId in payload."""
600+
@pytest.mark.parametrize(
601+
("deployment_id", "expected"),
602+
[
603+
(None, ""),
604+
("", ""),
605+
("/home/alice/model", ""),
606+
("alice@example.com", ""),
607+
("a" * 128, ""),
608+
("7cd46f928e324879b82a7cabd3c0ecd", ""),
609+
("7cd46f928e324879b82a7cabd3c0ecdZ", ""),
610+
("7cd46f928e324879b82a7cabd3c0ecde", "7cd46f928e324879b82a7cabd3c0ecde"),
611+
("7CD46F92-8E32-4879-B82A-7CABD3C0ECDE", "7cd46f928e324879b82a7cabd3c0ecde"),
612+
],
613+
)
614+
def test_deployment_id_validated_in_payloads(
615+
self, monkeypatch, reporter_session, deployment_id, expected
616+
):
617+
"""Initial, heartbeat, and exit payloads contain only normalized UUIDs or empty IDs."""
602618
monkeypatch.setenv("TRTLLM_DISAGG_ROLE", "context")
603-
monkeypatch.setenv("TRTLLM_DISAGG_DEPLOYMENT_ID", "abc123")
604-
605-
captured = {}
606-
607-
def fake_send(payload):
608-
captured.update(payload)
609-
610-
stop_event = threading.Event()
611-
stop_event.set()
619+
if deployment_id is None:
620+
monkeypatch.delenv("TRTLLM_DISAGG_DEPLOYMENT_ID", raising=False)
621+
else:
622+
monkeypatch.setenv("TRTLLM_DISAGG_DEPLOYMENT_ID", deployment_id)
623+
assert usage_lib._empty_event_snapshot()["deploymentId"] == expected
624+
captured = []
625+
stop_event = MagicMock()
626+
stop_event.wait.side_effect = [False, True]
612627

613628
with (
614-
patch.object(usage_lib, "_send_to_gxt", side_effect=fake_send),
629+
patch.object(usage_lib, "_send_to_gxt", side_effect=captured.append),
615630
patch.object(usage_lib, "_HEARTBEAT_STOP", stop_event),
616631
):
617632
usage_lib._background_reporter(None, None, "")
633+
usage_lib.report_exit(usage_lib.TerminalOutcome("clean", exit_code_known=True))
618634

619-
assert captured, "No payload was captured"
620-
params = captured["events"][0]["parameters"]
621-
assert params["disaggRole"] == "context"
622-
assert params["deploymentId"] == "abc123"
635+
events = [event for payload in captured for event in payload["events"]]
636+
assert [event["name"] for event in events] == [
637+
"trtllm_initial_report",
638+
"trtllm_heartbeat",
639+
"trtllm_exit_report",
640+
]
641+
for event in events:
642+
assert event["parameters"]["disaggRole"] == "context"
643+
assert event["parameters"]["deploymentId"] == expected
644+
if deployment_id and not expected:
645+
assert deployment_id not in json.dumps(captured)
623646

624647
def test_disagg_payload_includes_llm_api_config_json(self, monkeypatch, reporter_session):
625648
"""Disagg payloads retain sanitized LLM API config JSON fields."""
@@ -630,7 +653,8 @@ class _DisaggTelemetryArgs(BaseModel):
630653
)
631654

632655
monkeypatch.setenv("TRTLLM_DISAGG_ROLE", "generation")
633-
monkeypatch.setenv("TRTLLM_DISAGG_DEPLOYMENT_ID", "deploy123")
656+
deployment_id = usage_lib.uuid.uuid4()
657+
monkeypatch.setenv("TRTLLM_DISAGG_DEPLOYMENT_ID", deployment_id.hex)
634658

635659
captured = {}
636660

@@ -649,7 +673,7 @@ def fake_send(payload):
649673
assert captured, "No payload was captured"
650674
params = captured["events"][0]["parameters"]
651675
assert params["disaggRole"] == "generation"
652-
assert params["deploymentId"] == "deploy123"
676+
assert params["deploymentId"] == deployment_id.hex
653677
assert json.loads(params["llmApiConfigJson"]) == {"tensor_parallel_size": 2}
654678
meta = json.loads(params["llmApiConfigMetaJson"])
655679
assert meta["capture_succeeded"] is True
@@ -1286,12 +1310,17 @@ def test_terminal_event_uses_shared_session_id(self, monkeypatch, enable_telemet
12861310
assert event["parameters"]["ingressPoint"] == "cli_serve"
12871311
assert event["parameters"]["exitCode"] == 130
12881312

1313+
@pytest.mark.parametrize("valid_id", [False, True])
12891314
def test_terminal_event_contains_correlation_and_counter_snapshot(
1290-
self, monkeypatch, enable_telemetry
1315+
self, monkeypatch, enable_telemetry, valid_id
12911316
):
12921317
"""Terminal-only sessions carry enough state for independent analysis."""
12931318
monkeypatch.setenv("TRTLLM_DISAGG_ROLE", "ctx0")
1294-
monkeypatch.setenv("TRTLLM_DISAGG_DEPLOYMENT_ID", "deployment")
1319+
deployment_id = usage_lib.uuid.uuid4()
1320+
monkeypatch.setenv(
1321+
"TRTLLM_DISAGG_DEPLOYMENT_ID",
1322+
str(deployment_id) if valid_id else "/home/alice/deployment",
1323+
)
12951324
assert usage_lib.record_llm_initialization_attempt(default_usage_context="cli_serve")
12961325
usage_lib.record_llm_initialization_failure()
12971326

@@ -1308,7 +1337,8 @@ def test_terminal_event_contains_correlation_and_counter_snapshot(
13081337
)
13091338

13101339
params = sent[0]["events"][0]["parameters"]
1311-
assert params["deploymentId"] == "deployment"
1340+
assert params["deploymentId"] == (deployment_id.hex if valid_id else "")
1341+
assert "/home/alice/deployment" not in json.dumps(sent)
13121342
assert params["disaggRole"] == "ctx0"
13131343
assert params["llmInitializationAttempts"] == 1
13141344
assert params["llmInitializationFailures"] == 1

0 commit comments

Comments
 (0)