Skip to content

FFI Performance Metrics#849

Open
ladvoc wants to merge 3 commits intomainfrom
ladvoc/ffi-perf
Open

FFI Performance Metrics#849
ladvoc wants to merge 3 commits intomainfrom
ladvoc/ffi-perf

Conversation

@ladvoc
Copy link
Contributor

@ladvoc ladvoc commented Jan 24, 2026

Measures performance of the following in production:

  • send_event
  • store_handle
  • retrieve_handle
  • drop_handle
  • forward_audio_frame

This uses the metrics and metrics-logger crates. To see output, enable logging for metrics: RUST_LOG=metrics=debug. This does not log every individual data point but rather calculates aggregate statistics and logs every 10 seconds.

Note: this is intended to diagnose a specific performance issue and is not meant to be merged as-is.

Summary by CodeRabbit

  • Chores
    • Added performance instrumentation across audio and video processing pipelines to collect operation timing metrics.
    • Integrated metrics logging framework for systematic performance monitoring and telemetry collection.
    • Added new metrics-logger dependency to support performance data collection.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 24, 2026

📝 Walkthrough

Walkthrough

This pull request adds comprehensive performance instrumentation to the LiveKit FFI server by integrating metrics logging across audio and video processing paths. It captures timing metrics for frame capture and forwarding operations, plus handle management, using histogram-based measurements without altering public APIs or core functionality.

Changes

Cohort / File(s) Summary
Metrics Infrastructure Setup
livekit-ffi/Cargo.toml, livekit-ffi/src/server/mod.rs
Added metrics-logger dependency; initialized MetricsLogger recorder in FfiServer with periodic global recording. Added timing instrumentation (Instant-based) to send_event, store_handle, retrieve_handle, and drop_handle methods to record operation durations via histograms.
Audio Processing Instrumentation
livekit-ffi/src/server/audio_source.rs, livekit-ffi/src/server/audio_stream.rs
Added timing instrumentation around audio frame capture and forwarding paths using Instant::now() and histogram recording. Captures elapsed time for capture_audio_frame_task and forward_audio_frame operations.
Video Processing Instrumentation
livekit-ffi/src/server/video_source.rs, livekit-ffi/src/server/video_stream.rs
Added timing instrumentation around video frame capture and forwarding using Instant::now() and histogram recording for capture_video_frame and forward_video_frame metrics.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 Tick-tock, now we measure the hops and the stops,
Each frame that we capture gets a timestamp on top,
From audio to video, metrics we log,
No changes to function, just timing to record! 📊⏱️

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'FFI Performance Metrics' directly and clearly summarizes the main change: adding performance measurement instrumentation to FFI operations using metrics collection.

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

✨ Finishing touches
  • 📝 Generate docstrings

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

@ladvoc ladvoc marked this pull request as ready for review January 28, 2026 00:00
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
livekit-ffi/src/server/audio_source.rs (1)

77-121: Run rustfmt to fix the formatting failure.
CI reports a rustfmt error around Line 73; please run cargo fmt to resolve.

🤖 Fix all issues with AI agents
In `@livekit-ffi/Cargo.toml`:
- Line 40: The Cargo.toml adds metrics-logger = "0.22.1" which requires Rust
1.85+ but the crate lacks an explicit MSRV; update Cargo.toml to document the
requirement by adding rust-version = "1.85" in the package section, or
alternatively change the dependency to an older compatible metrics-logger
version (e.g., 0.21.x) to keep compatibility with older Rust toolchains—modify
either the package rust-version field or the metrics-logger version in
Cargo.toml accordingly.

In `@livekit-ffi/src/server/mod.rs`:
- Around line 15-29: The file has formatting issues flagged by CI; run rustfmt
(cargo fmt) on the file to normalize imports and spacing around the use
statements and items like AtomicU64, Ordering, DashMap, AudioProcessingModule,
AudioResampler, MetricsLogger and histogram; after running cargo fmt, re-check
that the use block and surrounding code (including any lines around the
referenced symbols) align with rustfmt’s output and commit the formatted file.
- Around line 104-109: The metrics recorder initialization in FfiServer::default
currently calls metrics::set_global_recorder(...).unwrap(), which panics if
invoked multiple times; wrap the recorder setup with a std::sync::Once (e.g., a
static ONCE) so the block that constructs
MetricsLogger::new(LogMode::Periodic(10), ...) and calls
metrics::set_global_recorder only runs once and ignores the "already set" error
instead of unwrapping; update FfiServer::default to use this Once guard when
initializing the global recorder to make repeated instantiation safe.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ff6041e and cb198f3.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • livekit-ffi/Cargo.toml
  • livekit-ffi/src/server/audio_source.rs
  • livekit-ffi/src/server/audio_stream.rs
  • livekit-ffi/src/server/mod.rs
  • livekit-ffi/src/server/video_source.rs
  • livekit-ffi/src/server/video_stream.rs
🧰 Additional context used
🧬 Code graph analysis (2)
livekit-ffi/src/server/mod.rs (1)
livekit-ffi/src/server/logger.rs (2)
  • new (44-56)
  • log (78-84)
livekit-ffi/src/server/audio_source.rs (3)
livekit-ffi/src/server/mod.rs (5)
  • std (230-230)
  • std (256-256)
  • handle (161-161)
  • handle (229-229)
  • handle (255-255)
livekit-ffi/src/server/requests.rs (23)
  • std (831-831)
  • std (848-848)
  • std (870-870)
  • std (909-909)
  • std (919-919)
  • std (941-941)
  • std (951-951)
  • server (70-70)
  • server (87-87)
  • server (98-98)
  • server (110-110)
  • server (121-121)
  • server (133-133)
  • server (144-144)
  • server (159-159)
  • server (174-174)
  • server (189-189)
  • server (207-208)
  • server (219-219)
  • server (228-229)
  • server (239-240)
  • server (250-251)
  • server (261-262)
libwebrtc/src/native/audio_source.rs (1)
  • std (130-130)
🪛 GitHub Actions: Rust Formatting
livekit-ffi/src/server/mod.rs

[error] 15-15: cargo fmt -- --check detected formatting issues in this file. Run 'cargo fmt' to fix code style.


[error] 26-26: cargo fmt -- --check detected formatting issues in this file. Run 'cargo fmt' to fix code style.


[error] 172-172: cargo fmt -- --check detected formatting issues in this file. Run 'cargo fmt' to fix code style.


[error] 261-261: cargo fmt -- --check detected formatting issues in this file. Run 'cargo fmt' to fix code style.

livekit-ffi/src/server/audio_source.rs

[error] 73-73: cargo fmt -- --check detected formatting issues in this file. Run 'cargo fmt' to fix code style.

🔇 Additional comments (11)
livekit-ffi/src/server/video_source.rs (2)

15-20: No issues to call out here.


64-81: Timing instrumentation looks good.
Line 64 through Line 81 cleanly scopes the capture duration and reports it without altering control flow.

livekit-ffi/src/server/audio_stream.rs (2)

16-23: No issues to call out here.


378-455: LGTM for the forward timing hook.
The added timing around frame handling is clean and doesn’t interfere with error paths.

livekit-ffi/src/server/video_stream.rs (2)

15-23: No issues to call out here.


138-175: Timing instrumentation looks good.
Line 138 through Line 175 records forwarding duration without changing behavior.

livekit-ffi/src/server/audio_source.rs (1)

15-18: No issues to call out here.

livekit-ffi/src/server/mod.rs (4)

176-187: LGTM for send_event timing.
Line 176 through Line 187 adds clean instrumentation without side effects.


205-210: LGTM for store_handle timing.
The timing hook is minimal and safely scoped.


224-237: LGTM for retrieve_handle timing.
The instrumentation is correctly placed after validation.


265-272: LGTM for drop_handle timing.
No behavioral changes beyond metrics.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

console-subscriber = { version = "0.1", features = ["parking_lot"], optional = true }
bytes = "1.10.1"
from_variants = "1.0.2"
metrics-logger = "0.22.1"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

metrics-logger rust crate version 0.22.1 breaking changes MSRV

💡 Result:

metrics-logger v0.22.1: breaking changes?

No—0.22.1 is a patch release and (by the metrics ecosystem’s changelog) the corresponding metrics 0.22.1 change is a fix (pinning ahash to avoid an unintended MSRV bump), not an API break. [1]

If you’re upgrading from 0.21.x → 0.22.x, the breaking changes are effectively in 0.22.0, not 0.22.1 (e.g., API/behavior changes around recorder installation and related additions like local recorders). [1]

MSRV for metrics-logger v0.22.1

metrics-logger 0.22.1 uses Rust 2024 edition, which means you need Rust ≥ 1.85 (Rust 1.85.0 is what stabilized the 2024 edition). [2][3]
(And Rust’s own Rust-2024 guidance explicitly calls out using rust-version = "1.85" when setting edition = "2024".) [4]

Notes on metrics MSRV (if you care about the underlying facade too)

The metrics crate’s 0.22.0 release notes indicate MSRV was bumped to Rust 1.65.0 at that time. [1]

Sources:
[1] metrics CHANGELOG (0.22.0 / 0.22.1 entries) https://docs.rs/crate/metrics/latest/source/CHANGELOG.md
[2] metrics-logger package page (shows “Uses new Rust 2024”) https://lib.rs/crates/metrics-logger
[3] Rust 1.85.0 announcement (“stabilizes the 2024 edition”) https://blog.rust-lang.org/2025/02/20/Rust-1.85.0/
[4] Rust 2024 beta post (mentions rust-version = "1.85" for edition = "2024") https://blog.rust-lang.org/2025/01/22/rust-2024-beta/


🏁 Script executed:

cat livekit-ffi/Cargo.toml

Repository: livekit/rust-sdks

Length of output: 1670


🏁 Script executed:

cat Cargo.toml

Repository: livekit/rust-sdks

Length of output: 1534


Verify MSRV compatibility: metrics-logger 0.22.1 requires Rust 1.85+

metrics-logger 0.22.1 is a stable patch release with no breaking changes, but it uses Rust 2024 edition, which requires Rust ≥ 1.85.0. Since livekit-ffi has no explicit rust-version declared, this creates a silent MSRV requirement that consumers may not be aware of. Either explicitly set rust-version = "1.85" in Cargo.toml to document this requirement, or consider using an earlier metrics-logger version (e.g., 0.21.x) if targeting older Rust versions.

🤖 Prompt for AI Agents
In `@livekit-ffi/Cargo.toml` at line 40, The Cargo.toml adds metrics-logger =
"0.22.1" which requires Rust 1.85+ but the crate lacks an explicit MSRV; update
Cargo.toml to document the requirement by adding rust-version = "1.85" in the
package section, or alternatively change the dependency to an older compatible
metrics-logger version (e.g., 0.21.x) to keep compatibility with older Rust
toolchains—modify either the package rust-version field or the metrics-logger
version in Cargo.toml accordingly.

Comment on lines 15 to +29
use std::{
error::Error,
sync::{
atomic::{AtomicU64, Ordering},
Arc,
Arc, atomic::{AtomicU64, Ordering}
},
thread,
time::Duration,
time::{Duration, Instant},
};

use dashmap::{mapref::one::MappedRef, DashMap};
use downcast_rs::{impl_downcast, Downcast};
use livekit::webrtc::{
native::apm::AudioProcessingModule, native::audio_resampler::AudioResampler, prelude::*,
};
use metrics_logger::{LogMode, MetricsLogger, metrics::{self, histogram}};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Run rustfmt for this file.
CI reports formatting issues at Lines 15/26/172/261; please run cargo fmt to address.

🧰 Tools
🪛 GitHub Actions: Rust Formatting

[error] 15-15: cargo fmt -- --check detected formatting issues in this file. Run 'cargo fmt' to fix code style.


[error] 26-26: cargo fmt -- --check detected formatting issues in this file. Run 'cargo fmt' to fix code style.

🤖 Prompt for AI Agents
In `@livekit-ffi/src/server/mod.rs` around lines 15 - 29, The file has formatting
issues flagged by CI; run rustfmt (cargo fmt) on the file to normalize imports
and spacing around the use statements and items like AtomicU64, Ordering,
DashMap, AudioProcessingModule, AudioResampler, MetricsLogger and histogram;
after running cargo fmt, re-check that the use block and surrounding code
(including any lines around the referenced symbols) align with rustfmt’s output
and commit the formatted file.

Comment on lines +104 to +109
let recorder = MetricsLogger::new(
LogMode::Periodic(10),
|logs| log::info!(target: "metrics", "{}", logs),
|err| log::error!(target: "metrics", "{}", err),
);
metrics::set_global_recorder(recorder).unwrap();
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the actual code in the file
find . -name "mod.rs" -path "*/livekit-ffi/src/server/*" | head -5

Repository: livekit/rust-sdks

Length of output: 133


🏁 Script executed:

# Find the file and examine the context around lines 104-109
find . -path "*/livekit-ffi/src/server/mod.rs" -type f

Repository: livekit/rust-sdks

Length of output: 92


🌐 Web query:

metrics crate Rust set_global_recorder behavior multiple calls panic

💡 Result:

In the metrics crate, set_global_recorder is single-assignment for the lifetime of the process: only the first successful call installs the global recorder; any later call does not replace it and instead returns an error. (docs.rs)

  • Behavior on multiple calls: the second (and subsequent) call returns Err(SetRecorderError(recorder_you_tried_to_set)). (docs.rs)
  • Panic? metrics::set_global_recorder itself does not panic; panics typically happen because caller code (or an exporter’s install() / build_and_install() helper) does set_global_recorder(...).unwrap() / .expect(...). (So “panic on multiple calls” is a usage pattern, not the API contract.) (docs.rs)
  • Also true for set_recorder(&'static dyn Recorder): it’s likewise “only once” and returns an error if already set. (prisma.github.io)
  • If you need different recorders in tests/scoped code: use with_local_recorder instead of trying to reset the global recorder. (docs.rs)

Citations:


🏁 Script executed:

# View the actual code around lines 104-109
sed -n '100,115p' ./livekit-ffi/src/server/mod.rs

Repository: livekit/rust-sdks

Length of output: 662


🏁 Script executed:

# Search for FfiServer definition and default implementation
rg "impl.*Default.*FfiServer|fn default" ./livekit-ffi/src/server/mod.rs -A 20

Repository: livekit/rust-sdks

Length of output: 933


🏁 Script executed:

# Search for FfiServer::default() calls in the codebase
rg "FfiServer::default|FfiServer\(\)" --type rust

Repository: livekit/rust-sdks

Length of output: 164


🏁 Script executed:

# Look for test files that might call FfiServer multiple times
fd "test|spec" --type f --path "*livekit-ffi*" | head -20

Repository: livekit/rust-sdks

Length of output: 294


🏁 Script executed:

# Search for all FfiServer usage patterns
rg "FfiServer" --type rust -B 2 -A 2

Repository: livekit/rust-sdks

Length of output: 47577


🏁 Script executed:

# Look for test files
find . -name "*.rs" -type f | xargs grep -l "mod test\|#\[test\]\|#\[cfg(test)\]" | grep -i ffi

Repository: livekit/rust-sdks

Length of output: 126


🏁 Script executed:

# Check lib.rs to see the lazy_static usage
sed -n '1,50p' ./livekit-ffi/src/lib.rs | head -50

Repository: livekit/rust-sdks

Length of output: 1481


Guard metrics recorder initialization against multiple calls to FfiServer::default().

metrics::set_global_recorder(...).unwrap() panics if called more than once. While the current codebase uses lazy_static! to ensure single initialization, the Default implementation is public and could be instantiated multiple times in tests or if the initialization pattern changes in the future. Use std::sync::Once to guard the initialization and ignore the "already set" error:

Suggested fix
-use std::{
+use std::{
     error::Error,
     sync::{
-        Arc, atomic::{AtomicU64, Ordering}
+        Arc, atomic::{AtomicU64, Ordering}, Once
     },
     thread,
     time::{Duration, Instant},
 };
+
+static METRICS_INIT: Once = Once::new();
@@
-        let recorder = MetricsLogger::new(
-            LogMode::Periodic(10),
-            |logs| log::info!(target: "metrics", "{}", logs),
-            |err| log::error!(target: "metrics", "{}", err),
-        );
-        metrics::set_global_recorder(recorder).unwrap();
+        METRICS_INIT.call_once(|| {
+            let recorder = MetricsLogger::new(
+                LogMode::Periodic(10),
+                |logs| log::info!(target: "metrics", "{}", logs),
+                |err| log::error!(target: "metrics", "{}", err),
+            );
+            let _ = metrics::set_global_recorder(recorder);
+        });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let recorder = MetricsLogger::new(
LogMode::Periodic(10),
|logs| log::info!(target: "metrics", "{}", logs),
|err| log::error!(target: "metrics", "{}", err),
);
metrics::set_global_recorder(recorder).unwrap();
METRICS_INIT.call_once(|| {
let recorder = MetricsLogger::new(
LogMode::Periodic(10),
|logs| log::info!(target: "metrics", "{}", logs),
|err| log::error!(target: "metrics", "{}", err),
);
let _ = metrics::set_global_recorder(recorder);
});
🤖 Prompt for AI Agents
In `@livekit-ffi/src/server/mod.rs` around lines 104 - 109, The metrics recorder
initialization in FfiServer::default currently calls
metrics::set_global_recorder(...).unwrap(), which panics if invoked multiple
times; wrap the recorder setup with a std::sync::Once (e.g., a static ONCE) so
the block that constructs MetricsLogger::new(LogMode::Periodic(10), ...) and
calls metrics::set_global_recorder only runs once and ignores the "already set"
error instead of unwrapping; update FfiServer::default to use this Once guard
when initializing the global recorder to make repeated instantiation safe.

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