perf: runtime performance pass, phase 1 (DB pragmas, batched scan writes)
Executes docs/superpowers/specs/2026-07-07-runtime-performance-pass-design.md: - database.rs: every pooled connection now runs WAL + synchronous=NORMAL + busy_timeout=5000 + foreign_keys=ON via with_init (in-memory pools get the latter two). Fixes the SQLITE_BUSY failure mode under the parallel scanner and makes FK cascades work on every connection, not just whichever one last ran the old one-shot pragma in delete_folder. - database.rs: prepare_cached for the per-video info_cache queries; new info_cache_put_many bulk upsert (single transaction) replaces the per-row autocommitted info_cache_put, with a round-trip unit test. - library.rs: enrich_with_cache collects cache misses and flushes them once per channel; title sort uses sort_by_cached_key. - app.rs: Title/ChannelAsc sorts use sort_by_cached_key instead of allocating two lowercased strings per comparison. - Cargo.toml: panic=abort in release (crash-hook still fires; Cargo forces unwind for test harnesses). PACKAGING.md documents the local RUSTFLAGS target-cpu=native opt-in; repo stays portable. - .gitignore: catacomb.db-wal / catacomb.db-shm sidecars. Verified: 146 tests pass (integration suite exercises the pragmas end-to-end), x86_64-pc-windows-gnu check green, live smoke shows journal_mode=wal + batched cache writes landing correct rows. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
891b1e0d3c
commit
d4bed019b2
7 changed files with 299 additions and 34 deletions
|
|
@ -449,6 +449,10 @@ fn collect_raw_videos(entries: impl Iterator<Item = std::fs::DirEntry>) -> Vec<R
|
|||
}
|
||||
|
||||
fn enrich_with_cache(raws: Vec<RawVideo>, cache: Option<&Database>) -> Vec<Video> {
|
||||
// Cache misses are collected here and flushed in ONE transaction after
|
||||
// the loop (`info_cache_put_many`). On a cold cache every video is a
|
||||
// miss, so per-row autocommitted upserts would pay one commit per video.
|
||||
let mut cache_misses: Vec<(String, u64, Option<f64>, bool, Option<String>)> = Vec::new();
|
||||
let mut videos: Vec<Video> = raws.into_iter().map(|raw| {
|
||||
// Parse info.json once for both duration and chapter presence.
|
||||
// The cache (if provided) lets us skip the read+JSON parse when
|
||||
|
|
@ -485,8 +489,8 @@ fn enrich_with_cache(raws: Vec<RawVideo>, cache: Option<&Database>) -> Vec<Video
|
|||
(dur, chap, date)
|
||||
})
|
||||
.unwrap_or((None, false, None));
|
||||
if let (Some(mt), Some(db)) = (mtime, cache) {
|
||||
db.info_cache_put(&path_str, mt, parsed.0, parsed.1, parsed.2.as_deref());
|
||||
if let (Some(mt), Some(_)) = (mtime, cache) {
|
||||
cache_misses.push((path_str.into_owned(), mt, parsed.0, parsed.1, parsed.2.clone()));
|
||||
}
|
||||
parsed
|
||||
})
|
||||
|
|
@ -516,7 +520,11 @@ fn enrich_with_cache(raws: Vec<RawVideo>, cache: Option<&Database>) -> Vec<Video
|
|||
upload_date,
|
||||
}
|
||||
}).collect();
|
||||
videos.sort_by_key(|v| v.title.to_lowercase());
|
||||
if let Some(db) = cache {
|
||||
db.info_cache_put_many(&cache_misses);
|
||||
}
|
||||
// sort_by_cached_key: lowercase each title once, not once per comparison.
|
||||
videos.sort_by_cached_key(|v| v.title.to_lowercase());
|
||||
videos
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue