use macrame::metrics::CommandKind;
use macrame::prelude::*;
const TS: &str = "2026-01-01T00:00:00.000000Z";
const OPEN: &str = "9999-12-31T23:59:59.999999Z";
async fn build(dir: &std::path::Path, edges: usize) -> (std::path::PathBuf, Database) {
let path = dir.join(format!("analyze_{edges}.db"));
let db = Database::open_with_cadence(&path, None).await.unwrap();
let concepts: Vec<_> = (0..=edges)
.map(|i| ConceptUpsert::new(format!("c{i:06}"), format!("C{i}")).valid_from(TS))
.collect();
db.write_concepts(concepts).await.unwrap();
let hub = edges / 4;
let mut assertions = Vec::with_capacity(edges);
for i in 1..=hub {
assertions.push(
EdgeAssertion::new("c000000", format!("c{i:06}"), "LINKS")
.valid_from(TS)
.valid_to(OPEN),
);
}
for i in hub + 1..edges {
assertions.push(
EdgeAssertion::new(format!("c{i:06}"), format!("c{:06}", i + 1), "LINKS")
.valid_from(TS)
.valid_to(OPEN),
);
}
db.bulk_import(assertions).await.unwrap();
(path, db)
}
async fn crate_hold(db: &Database) -> (u64, u64, std::time::Duration) {
let before = analyze_turns(db);
db.analyze().await.unwrap();
let snap = db.metrics();
let k = snap.kinds.iter().find(|k| k.kind == CommandKind::Analyze);
(
before,
k.map(|k| k.turns).unwrap_or(0),
k.map(|k| k.longest).unwrap_or_default(),
)
}
fn analyze_turns(db: &Database) -> u64 {
db.metrics()
.kinds
.iter()
.find(|k| k.kind == CommandKind::Analyze)
.map(|k| k.turns)
.unwrap_or(0)
}
async fn bare_analyze(path: &std::path::Path, limit: u32) -> (i64, std::time::Duration) {
let db = libsql::Builder::new_local(path).build().await.unwrap();
let conn = db.connect().unwrap();
let _ = conn.query("PRAGMA journal_mode = WAL", ()).await.unwrap();
let _ = conn
.query(&format!("PRAGMA analysis_limit = {limit}"), ())
.await
.unwrap();
let readback: i64 = conn
.query("PRAGMA analysis_limit", ())
.await
.unwrap()
.next()
.await
.unwrap()
.map(|r| r.get(0).unwrap())
.unwrap_or(-1);
let _ = conn.execute("DROP TABLE IF EXISTS sqlite_stat1", ()).await;
let t = std::time::Instant::now();
conn.execute("ANALYZE", ()).await.unwrap();
(readback, t.elapsed())
}
#[tokio::main]
async fn main() {
let dir = std::env::temp_dir().join(format!("macrame_analyze_hold_{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
println!("edges crate hold limit=off limit=400 readback");
let mut on = Vec::new();
for edges in [10_000usize, 40_000] {
let (path, db) = build(&dir, edges).await;
let (before, after, held) = crate_hold(&db).await;
assert_eq!(
(before, after),
(0, 1),
"the Analyze counter moved {before} -> {after}: something other than \
this call ran ANALYZE or optimize, so `longest` is not this hold"
);
let snap = db.metrics();
let violations: Vec<(String, u64)> = snap
.budget_violations()
.iter()
.map(|k| (k.kind.as_str().to_string(), k.over_budget))
.collect();
println!(" over budget after analyze(): {violations:?}");
db.close().await.unwrap();
let (_, off) = bare_analyze(&path, 0).await;
let (readback, capped) = bare_analyze(&path, 400).await;
println!("{edges:<8} {held:<12?} {off:<11?} {capped:<11?} {readback}");
on.push((edges, held, off, capped));
}
let (n0, held0, off0, cap0) = on[0];
let (n1, held1, off1, cap1) = on[1];
let rows = n1 as f64 / n0 as f64;
println!();
println!("table grew {rows:.0}x over this range:");
println!(
" uncapped ANALYZE grew {:.1}x, capped grew {:.1}x, the crate's hold grew {:.1}x",
off1.as_secs_f64() / off0.as_secs_f64(),
cap1.as_secs_f64() / cap0.as_secs_f64(),
held1.as_secs_f64() / held0.as_secs_f64(),
);
println!(
" the cap is worth {:.1}x at {n0} and {:.1}x at {n1}",
off0.as_secs_f64() / cap0.as_secs_f64(),
off1.as_secs_f64() / cap1.as_secs_f64(),
);
println!();
println!(
"The crate's hold tracks the capped arm, not the uncapped one, so the \
pragma is in force on the write connection.\nIt is a constant factor, \
not a bound: what is left still grows with the table, and at {n1} edges \
the hold is {held1:?} against a {:?} budget.",
macrame::CHUNK_BUDGET
);
let _ = std::fs::remove_dir_all(&dir);
}