//! What a repository's pulls and issues cost a page: read from the index //! and assembled on every ask, as each page did, against what the index //! keeps of them, and what writes elsewhere and to it do to that. //! //! cargo run --release -p knotless-serve --example forge -- //! //! The index is written to, so give it a copy: prod's, say, for a busy //! repository's numbers. use knotless::records::{RepoRecord, nsid}; use knotless_serve::index::Index; use serde_json::json; use std::path::PathBuf; use std::time::{Duration, Instant}; /// Runs `f` until a second has passed, at least ten times; the median and /// the slowest one in a hundred. fn time(mut f: impl FnMut() -> T) -> (Duration, Duration) { let mut runs = Vec::new(); let start = Instant::now(); while runs.len() < 10 || start.elapsed() < Duration::from_secs(1) { let t = Instant::now(); std::hint::black_box(f()); runs.push(t.elapsed()); } runs.sort(); (runs[runs.len() / 2], runs[runs.len() * 99 / 100]) } fn ms(d: Duration) -> String { format!("{:.3}", d.as_secs_f64() * 1000.0) } fn row(what: &str, (median, slow): (Duration, Duration)) { println!("{what:<44} {:>9} {:>9}", ms(median), ms(slow)); } fn main() { let args: Vec = std::env::args().skip(1).collect(); let [path, uri] = &args[..] else { eprintln!("usage: forge "); std::process::exit(2); }; let index = Index::open(&PathBuf::from(path)).expect("an index"); let value = index.repo(uri).expect("readable").expect("the repository"); let record: RepoRecord = serde_json::from_value(value).expect("a repository record"); let did = uri.trim_start_matches("at://").split('/').next().unwrap(); let key = record.key.clone(); let entries = index.forge(did, &key).unwrap(); let items = knotless::forge::assemble(&record, did, &entries); println!( "{uri}: {} records, {} pulls and issues\n", entries.len(), items.len() ); println!("{:<44} {:>9} {:>9}", "ms", "median", "p99"); let before = time(|| { let entries = index.forge(did, &key).unwrap(); knotless::forge::assemble(&record, did, &entries) }); row("read and assembled, as every ask was", before); let kept = time(|| index.assembled(did, &key, &record).unwrap()); row("kept", kept); // The network writing about another repository leaves it kept. let mut n = 0; let elsewhere = time(|| { n += 1; let other = json!({"uri": "at://did:plc:other/dev.knotless.repo/x", "key": "x"}); let said = json!({"repo": other, "subject": "at://did:plc:other/dev.knotless.issue/1", "body": "hi", "createdAt": "2026-10-29T00:00:00Z"}); index .put("did:plc:other", nsid::COMMENT, &format!("c{n}"), &said) .unwrap(); index.assembled(did, &key, &record).unwrap() }); row("kept, with a write elsewhere before each", elsewhere); let accounts = time(|| { n += 1; index .set_status(&format!("did:plc:gone{n}"), "deactivated") .unwrap(); index.assembled(did, &key, &record).unwrap() }); row("kept, with an account elsewhere deactivated", accounts); // A comment on it is read and assembled again. let repo = json!({"uri": uri, "key": key}); let on = time(|| { n += 1; let said = json!({"repo": repo, "subject": items[0].uri, "body": "hi", "createdAt": "2026-10-29T00:00:00Z"}); index .put("did:plc:bench", nsid::COMMENT, &format!("c{n}"), &said) .unwrap(); index.assembled(did, &key, &record).unwrap() }); row("with a comment on it before each", on); // What a pull's files page asked before, each assembling them anew: // getRepo once, getThread three times, getPullDiff twice. let page = |one: Duration| one * 6; println!( "\na pull's files page: {} ms assembling before, {} ms kept", ms(page(before.0)), ms(page(kept.0)) ); }