use std::sync::Arc;
use std::time::{Duration, Instant};
use macrame::graph::EdgeAssertion;
use macrame::util::clock::parse_iso8601_utc;
use macrame::util::FakeClock;
use macrame::{ConceptUpsert, Database};
const EPOCH: &str = "2026-01-01T00:00:00.000000Z";
const OPEN: &str = "9999-12-31T23:59:59.999999Z";
const GENERATION: Duration = Duration::from_secs(3_600);
async fn seed(db: &Database, clock: &FakeClock, keys: usize, generations: usize) {
let nodes = keys + 1;
for chunk in (0..nodes).collect::<Vec<_>>().chunks(2_000) {
db.write_concepts(
chunk
.iter()
.map(|i| ConceptUpsert::new(format!("c{i:07}"), "n").valid_from(EPOCH))
.collect(),
)
.await
.unwrap();
}
for gen in 0..generations {
let batch: Vec<_> = (0..keys)
.map(|k| {
EdgeAssertion::new(format!("c{k:07}"), format!("c{:07}", k + 1), "LINKS")
.valid_from(EPOCH)
.valid_to(OPEN)
.weight(1.0 + gen as f64)
})
.collect();
for chunk in batch.chunks(2_000) {
db.bulk_import(chunk.to_vec()).await.unwrap();
}
clock.advance(GENERATION);
}
}
struct Run {
sessions: usize,
archived: usize,
total_ms: f64,
longest_hold_ms: f64,
}
async fn measure(keys: usize, generations: usize, window: Option<Duration>) -> Run {
let dir = tempfile::TempDir::new().unwrap();
let path = dir.path().join("w.db");
let clock = Arc::new(FakeClock::new(parse_iso8601_utc(EPOCH).unwrap()));
let db = Database::open_with_clock(&path, None, clock.clone())
.await
.unwrap();
seed(&db, &clock, keys, generations).await;
let cutoff = clock.peek();
let before = db.metrics();
let start = Instant::now();
let reports = match window {
Some(w) => db.archive_windowed(&cutoff, w).await.unwrap(),
None => vec![db.archive(&cutoff).await.unwrap()],
};
let total_ms = start.elapsed().as_secs_f64() * 1000.0;
let after = db.metrics();
let longest_hold_ms = archive_longest_ms(&before, &after);
db.close().await.unwrap();
Run {
sessions: reports.len(),
archived: reports.iter().map(|r| r.links_archived).sum(),
total_ms,
longest_hold_ms,
}
}
fn archive_longest_ms(
before: ¯ame::metrics::MetricsSnapshot,
after: ¯ame::metrics::MetricsSnapshot,
) -> f64 {
let kind = |s: ¯ame::metrics::MetricsSnapshot| {
s.kinds
.iter()
.find(|k| k.kind == macrame::metrics::CommandKind::Archive)
.unwrap()
.clone()
};
assert_eq!(kind(before).turns, 0, "the fixture archived during seeding");
kind(after).longest.as_secs_f64() * 1000.0
}
#[tokio::main]
async fn main() {
println!("libSQL 0.9.30. `hold ms` is the longest single archive turn, read");
println!("from the actor's own per-kind high-water mark (T1.4).\n");
println!(
"{:>7} {:>6} {:>16} {:>9} {:>10} {:>11} {:>10}",
"keys", "gens", "window", "sessions", "archived", "total ms", "hold ms"
);
for (keys, generations) in [(2_000usize, 8usize), (8_000, 8)] {
let whole = measure(keys, generations, None).await;
println!(
"{:>7} {:>6} {:>16} {:>9} {:>10} {:>11.1} {:>10.1}",
keys,
generations,
"-- one session",
whole.sessions,
whole.archived,
whole.total_ms,
whole.longest_hold_ms
);
for hours in [4u64, 2, 1] {
let w = Duration::from_secs(3_600 * hours);
let run = measure(keys, generations, Some(w)).await;
println!(
"{:>7} {:>6} {:>16} {:>9} {:>10} {:>11.1} {:>10.1}",
keys,
generations,
format!("{hours}h"),
run.sessions,
run.archived,
run.total_ms,
run.longest_hold_ms
);
assert_eq!(
run.archived, whole.archived,
"windowing changed *what* was archived, which would make every \
timing above meaningless"
);
}
println!();
}
}