Measure the folder walk against a real tree, not a claim
The folder connector declares `LocalEtags`, which means the engine walks the whole library on every scan with no pruning. That is the honest capability, and the argument for it being affordable was so far an assertion about `stat` versus `PROPFIND`. `--example scan` runs the real path — `dr_sync::scan` over the connector, then a ranged read of the kind the thumbnail worker makes. Read-only; it never writes into the folder it is pointed at. 2,299 images across 233 directories in 137 ms, and 380 across 13 in 29 ms. Against 34.1 s for 17,185 RAWs over WebDAV *with* pruning available. Recorded in docs/storage.md §5.2 and ARCH §8.4a, because a capability trade-off argued from a number nobody measured is the kind that gets quietly reversed later.
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
//! Scan a real folder through the engine, and read a preview out of it.
|
||||
//!
|
||||
//! ```text
|
||||
//! cargo run -p dr-sync-folder --example scan -- /path/to/photos
|
||||
//! ```
|
||||
//!
|
||||
//! Exercises the same code the application runs: `dr_sync::scan` driving the
|
||||
//! folder connector, then a ranged `get` of the kind the thumbnail worker
|
||||
//! makes. Reads only — it never writes into the folder it is pointed at.
|
||||
use std::collections::HashMap;
|
||||
|
||||
use dr_sync::{RemoteBackend, RemoteId, RemotePath};
|
||||
use dr_sync_folder::FolderBackend;
|
||||
use dr_types::FormatFilter;
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() {
|
||||
let Some(root) = std::env::args().nth(1) else {
|
||||
eprintln!("usage: scan <folder>");
|
||||
std::process::exit(2);
|
||||
};
|
||||
|
||||
let backend = match FolderBackend::new(&root) {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
eprintln!("{e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let caps = backend.capabilities();
|
||||
println!("{} at {root}", backend.name());
|
||||
println!(
|
||||
" strategy: {}",
|
||||
dr_sync::SyncStrategy::for_capabilities(caps).describe()
|
||||
);
|
||||
|
||||
let started = std::time::Instant::now();
|
||||
let result = dr_sync::scan(
|
||||
&backend,
|
||||
&RemotePath::root(),
|
||||
&FormatFilter::all(),
|
||||
&HashMap::new(),
|
||||
|_| {},
|
||||
)
|
||||
.await
|
||||
.expect("scan");
|
||||
|
||||
println!(
|
||||
" {} image(s) in {} director(ies), {:?}",
|
||||
result.images.len(),
|
||||
result.progress.directories_listed,
|
||||
started.elapsed()
|
||||
);
|
||||
|
||||
let Some(first) = result.images.first() else {
|
||||
return;
|
||||
};
|
||||
println!(
|
||||
" first: {} ({} bytes) id {:?}",
|
||||
first.path, first.size, first.id
|
||||
);
|
||||
|
||||
// The shape of request the thumbnail worker makes: a header window, not
|
||||
// the whole file.
|
||||
let head = backend
|
||||
.get(&RemoteId::Path(first.path.clone()), Some(0..65536))
|
||||
.await
|
||||
.expect("ranged read");
|
||||
println!(" read {} header bytes", head.len());
|
||||
}
|
||||
Reference in New Issue
Block a user