Skip to content

feat(libmoq, moq-ffi): expose connection stats#1783

Open
kixelated wants to merge 2 commits into
mainfrom
claude/zen-bardeen-74184a
Open

feat(libmoq, moq-ffi): expose connection stats#1783
kixelated wants to merge 2 commits into
mainfrom
claude/zen-bardeen-74184a

Conversation

@kixelated

Copy link
Copy Markdown
Collaborator

Summary

Adds a point-in-time snapshot of the underlying QUIC/WebTransport connection (RTT, send/receive bandwidth estimates, byte/packet counters) to both FFI layers. libmoq itself previously exported no stats/version API, so callers had no way to read MoQ-level connection health.

The transport already exposed these metrics via web_transport_trait::Stats; moq-net just wasn't surfacing them. This plumbs a snapshot through three layers and out through both FFI surfaces.

What changed

  • rs/moq-net: new public ConnectionStats struct (#[non_exhaustive], every field Option since availability is backend/state-dependent) and Session::stats(). Reads the transport's QUIC counters and fills estimated_recv_rate from the existing MoQ PROBE bandwidth consumer.
  • rs/moq-native: a cloneable ConnectionStatsReader (Reconnect::stats()). The live session lives inside the reconnect loop, so the reader snapshots the current connection and returns None while reconnecting.
  • rs/libmoq: new C API moq_session_stats(session, &dst) filling a moq_connection_stats struct. Each metric has a *_valid flag, because a false flag is not the same as a zero value. The reconnect loop is now built up front in connect() so a stats reader can be grabbed before it moves into the spawned task.
  • rs/moq-ffi: MoqSession.stats() returning a MoqConnectionStats uniffi record with Option<u64> fields (→ native optionals in Python/Swift/Kotlin/Go). The py/swift/kt/go wrappers expose the uniffi MoqSession directly, so it surfaces automatically with no wrapper code.
  • docs: a "Connection statistics" section in the C doc and stats() snippets in the Swift/Kotlin quickstarts.

Stats exposed

RTT, estimated send rate (congestion-controller bandwidth), estimated recv rate (MoQ PROBE), and bytes/packets sent/received/lost.

Notes for reviewers

  • Public API changes (all additive / non-breaking): moq_net::ConnectionStats + Session::stats(); moq_native::ConnectionStatsReader + Reconnect::stats(); moq_connection_stats + moq_session_stats() in libmoq (regenerated moq.h); MoqConnectionStats + MoqSession::stats() in moq-ffi. Hence targeting main.
  • Native QUIC reports every metric; the browser WebTransport reports few or none — hence the per-field optionality.
  • Out of scope / follow-ups: per-track stats (a larger moq-net broadcast-aggregation effort) and the js/net browser mirror.

Test plan

  • cargo check/build across moq-net, moq-native, libmoq, moq-ffi
  • cargo clippy --all-targets clean (via nix)
  • cargo test passes for all four crates
  • cargo fmt --check clean
  • moq.h regenerates with moq_connection_stats + moq_session_stats

(Written by Claude)

Surface a point-in-time snapshot of the underlying QUIC/WebTransport
connection (RTT, send/receive bandwidth estimates, byte/packet counters)
through both FFI layers. The transport already exposed these via
web_transport_trait::Stats; moq-net just wasn't surfacing them.

- moq-net: new public ConnectionStats struct (non_exhaustive, all fields
  Option) and Session::stats(), reading the transport counters plus the
  MoQ PROBE recv-bandwidth estimate.
- moq-native: ConnectionStatsReader + Reconnect::stats() so the live
  session inside the reconnect loop can be polled; returns None while
  reconnecting.
- libmoq: moq_session_stats(session, &dst) filling a moq_connection_stats
  struct. Each metric carries a *_valid flag since availability is
  backend-dependent (a false flag is not a zero value).
- moq-ffi: MoqSession.stats() returning a MoqConnectionStats record with
  Option<u64> fields, surfaced automatically by the py/swift/kt/go
  wrappers.
- docs: connection-stats section in the C doc, stats() snippets in the
  Swift/Kotlin quickstarts.

Additive (non-breaking) API changes. Per-track stats and the js/net
browser mirror are left as follow-ups.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 18, 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1d71e199-ab91-4bca-a9d2-3a36412bc765

📥 Commits

Reviewing files that changed from the base of the PR and between 1efa1d9 and 000cec9.

📒 Files selected for processing (2)
  • rs/libmoq/src/api.rs
  • rs/moq-ffi/src/session.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • rs/moq-ffi/src/session.rs
  • rs/libmoq/src/api.rs

Walkthrough

This pull request introduces connection statistics reporting across the full library stack. A new ConnectionStats struct is defined in moq-net with optional fields for RTT, bandwidth estimates, byte counts, and packet/datagram counters. The reconnect loop in moq-native gains a ConnectionStatsReader that tracks the live session and returns a snapshot only when connected. The libmoq session layer threads the reader into TaskEntry and exposes Session::stats(id). A #[repr(C)] moq_connection_stats struct with per-field *_valid flags and a corresponding moq_session_stats extern "C" function are added to the C FFI surface. A parallel MoqConnectionStats record and MoqSession::stats() method are added to the UniFFI surface for Kotlin and Swift. Documentation examples are added for all three language bindings.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: exposing connection statistics through the libmoq and moq-ffi libraries.
Description check ✅ Passed The description provides comprehensive context about the purpose, implementation details across multiple crates, and testing performed, all directly related to the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/zen-bardeen-74184a

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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 `@rs/libmoq/src/api.rs`:
- Around line 95-127: Add documentation comments using `///` for all the boolean
`*_valid` fields in the struct that are currently undocumented. These fields
include send_rate_valid, recv_rate_valid, bytes_sent_valid,
bytes_received_valid, bytes_lost_valid, packets_sent_valid,
packets_received_valid, and packets_lost_valid. Each `*_valid` field should have
a doc comment explaining that it indicates whether the corresponding measurement
field is valid, to ensure complete API documentation for C consumers.

In `@rs/moq-ffi/src/session.rs`:
- Line 181: The rtt_us field assignment uses a lossy cast from u128 to u64 via
the as operator, which can silently truncate large duration values. Replace the
direct as u64 cast in the map closure with a checked conversion using try_into()
method, which will return a Result that can be properly handled to prevent
overflow instead of wrapping.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4f4c83f5-6022-41cc-b316-65d839cde832

📥 Commits

Reviewing files that changed from the base of the PR and between 4209d7a and 1efa1d9.

📒 Files selected for processing (8)
  • doc/lib/c/index.md
  • doc/lib/kt/index.md
  • doc/lib/swift/index.md
  • rs/libmoq/src/api.rs
  • rs/libmoq/src/session.rs
  • rs/moq-ffi/src/session.rs
  • rs/moq-native/src/reconnect.rs
  • rs/moq-net/src/session.rs

Comment thread rs/libmoq/src/api.rs
Comment thread rs/moq-ffi/src/session.rs Outdated
Address review feedback:
- Document every `*_valid` flag in `moq_connection_stats` so the generated
  C header is fully documented.
- Convert `Duration::as_micros()` (u128) to u64 via checked `try_from`
  instead of a lossy `as` cast, in both the libmoq and moq-ffi mappings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@arielmol

Copy link
Copy Markdown
Contributor

Very welcome stats for gsteamer moqsink.

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.

2 participants