Skip to content

APP-14871: Report WebRTC connection metadata after dialing#185

Draft
Daniel Botros (danielbotros) wants to merge 1 commit into
mainfrom
APP-14871-connection-metadata-telemetry
Draft

APP-14871: Report WebRTC connection metadata after dialing#185
Daniel Botros (danielbotros) wants to merge 1 commit into
mainfrom
APP-14871-connection-metadata-telemetry

Conversation

@danielbotros

@danielbotros Daniel Botros (danielbotros) commented Jul 9, 2026

Copy link
Copy Markdown
Member

Description

APP-14871 Track coturn usage / WebRTC client connection telemetry — the Python/C++ SDK (rust-utils) port of the Go SDK's dial telemetry in goutils #583. App-side receiver in app #11316.

Depends on goutils #583: the SignalingService.ReportConnectionMetadata proto additions. The committed src/gen stubs were generated from that PR's branch; once it merges and publishes to the BSR, make buf reproduces them.

Client (maybe_connect_via_webrtc) — after a WebRTC dial succeeds or fails, reports best-effort to the signaling server it dialed through:

  • reached_stage: the furthest dial checkpoint, advanced as the dial progresses (SIGNALING_CONNECTED → CONFIG_FETCHED → OFFER_SENT → ANSWER_RECEIVED → ICE_CONNECTED → DTLS_CONNECTED → READY). READY means success; any earlier value is where a failed dial stopped. Tracked by a forward-only atomic (DialStageTracker) since checkpoints advance from the dial task, the ICE state callback, and the answer-processing task.
  • local / remote: the selected ICE candidate pair classified per side as host / stun / relay from the peer connection stats (nominated + succeeded pair — webrtc-rs's get_selected_candidate_pair is opaque). Relay candidates carry the relay address so app can attribute the provider (Viam coturn vs Twilio).
  • failure_code: the tonic status code of a failed dial (first status in the error chain; non-gRPC failures map to Unknown). Set only on failure.
  • signaling_path: classified from the signaling server address the channel actually dialed (app.viam.com / app.viam.devcloud_signaled, anything else → local). WebRTC is never dialed over mDNS here (mDNS connections are direct gRPC, RSDK-14026), so mdns_local is never reported by this SDK.
  • duration_ms (dial start → ready/failure) and sdk_type=python_cpp / sdk_version (the rust-utils crate version; the FFI layer cannot distinguish Python from C++ callers, hence the single combined type).
  • The reporting code lives in its own src/rpc/dial_report.rs. The send is spawned detached with a 5s timeout so it never delays the caller — in particular a failed WebRTC dial's direct-connection fallback proceeds immediately.

Reporting semantics vs Go — one report per WebRTC dial attempt, success or failure:

  • Go suppresses failure reports when the logical dial succeeds because its parallel mDNS/cloud race produces spurious Canceled failures from losing paths. rust-utils has no parallel WebRTC race (the mDNS branch never dials WebRTC), so there is nothing to dedup — a failed attempt's report is the signal, even when the dial then falls back to a direct gRPC connection. Suppressing on fallback success would hide nearly all WebRTC failures from Python/C++, since the fallback usually works.
  • An Unimplemented failure (remote has no WebRTC signaler) is fallback control flow, not a WebRTC failure, and is never reported — mirroring Go's ErrNoWebRTCSignaler handling.
  • A dial cancelled by the mDNS race winning drops the in-flight future before any report is built, so cancelled attempts stay silent, matching Go.

Tests — unit coverage in dial_report.rs for stage-tracker forward-only advancement, signaling-path classification, candidate/pair classification (host/stun/relay, relay address, no-pair, missing-candidate), and failure-code extraction.

Testing

  • cargo build, cargo test --lib (10/10), clippy and rustfmt clean on touched files.
  • E2E against a goutils echo server built from goutils #583, with the server temporarily logging received reports:
    • Ran the existing tests/echo_test.rs suite (6/6 pass). Exactly one ReportConnectionMetadata per WebRTC dial, OK response.
    • Success report: local/remote host↔host, sdk_type=python_cpp, reached_stage=READY, duration_ms=580, signaling_path=local, sdk_version=0.6.0.
    • Forced-relay failure (no reachable TURN): dial fell back to direct as before, and reported reached_stage=ANSWER_RECEIVED, failure_code=2 (Unknown), signaling_path=local, candidates unspecified.

🤖 Generated with Claude Code

Port of the Go SDK's WebRTC dial telemetry (goutils#583) for the
Python/C++ SDKs. After a WebRTC dial finishes — success or failure —
the client best-effort reports to the signaling server it dialed
through: the furthest dial stage reached, the gRPC failure code, the
dial duration, how the dial was signaled (cloud vs local), the
selected ICE candidate pair per side (host/stun/relay + relay
address), and the SDK type/version.

Proto stubs generated from the goutils PR branch; regenerate with
`make buf` once goutils#583 lands on the BSR.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant