diff --git a/services/receiver/src/rd_poll.rs b/services/receiver/src/rd_poll.rs index 1466a42c..4ce123f5 100644 --- a/services/receiver/src/rd_poll.rs +++ b/services/receiver/src/rd_poll.rs @@ -35,11 +35,11 @@ pub enum PollOutcome { /// Compute the `(mtime, size)` signature of all active RD files in `dir`, or `None` if /// any file's metadata cannot be read (missing file, RD closed, share /// unmounted). -fn signature(dir: &Path) -> Option { +async fn signature(dir: &Path) -> Option { let files = rd_dbf::resolve_rd_files(dir).ok()?; let mut sig = Vec::with_capacity(files.len()); for file in files { - let meta = std::fs::metadata(dir.join(file)).ok()?; + let meta = tokio::fs::metadata(dir.join(file)).await.ok()?; sig.push((meta.modified().ok()?, meta.len())); } Some(sig) @@ -74,7 +74,7 @@ pub async fn poll_once( dir: &Path, last: &mut Option, ) -> PollOutcome { - let sig = signature(dir); + let sig = signature(dir).await; if let (Some(new), Some(old)) = (sig.as_ref(), last.as_ref()) && new == old { diff --git a/services/receiver/tests/bench_rd_poll.rs b/services/receiver/tests/bench_rd_poll.rs new file mode 100644 index 00000000..e87f046f --- /dev/null +++ b/services/receiver/tests/bench_rd_poll.rs @@ -0,0 +1,49 @@ +use receiver::control_api::AppState; +use receiver::db::Db; +use receiver::rd_dbf::RD_FILES; +use receiver::rd_poll::{PollOutcome, poll_once}; +use std::path::{Path, PathBuf}; +use std::time::Instant; + +fn fixtures_dir() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests") + .join("fixtures") + .join("rd") +} + +fn copy_fixtures_to(dir: &Path) { + for file in RD_FILES { + std::fs::copy(fixtures_dir().join(file), dir.join(file)).unwrap(); + } +} + +#[tokio::test] +async fn bench_rd_poll_signature() { + let (state, _rx) = AppState::new(Db::open_in_memory().unwrap(), "recv".to_owned()); + let tmp = tempfile::tempdir().unwrap(); + copy_fixtures_to(tmp.path()); + + let mut last = None; + + // Warm-up / initial import pass + let first = poll_once(&state, tmp.path(), &mut last).await; + assert!(matches!(first, PollOutcome::Imported(_))); + + let iterations = 1000; + let start = Instant::now(); + + for _ in 0..iterations { + let outcome = poll_once(&state, tmp.path(), &mut last).await; + assert!(matches!(outcome, PollOutcome::Skipped)); + } + + let elapsed = start.elapsed(); + let avg = elapsed / iterations; + + println!("\n=================================================="); + println!("BENCHMARK: rd_poll change detection (signature check):"); + println!("Total time for {iterations} iterations: {:?}", elapsed); + println!("Average time per poll pass: {:?}", avg); + println!("==================================================\n"); +}