//! Cold-start bug audit for the artist-page pipeline. Companion to //! `artist_page_bench` (latency) — this one checks INVARIANTS across a spread //! of artist shapes and prints a statistics row per artist: //! //! - overview / page / identity wall-clock, run the way the frontend runs //! them (all three overlapped, one fresh client per artist, no disk cache) //! - album and popular-row counts at each stage //! - listeners coverage (the Last.fm anchor working end-to-end) //! - release-date coverage (the MusicBrainz dates pass reaching the page) //! - BUG lines for anything that violates an invariant //! //! Artists are audited SEQUENTIALLY — each fresh client has its own //! MusicBrainz pacer, and overlapping artists would multiply request rate //! against the 1 req/s policy (and contaminate the numbers, see the //! paced-probe rule). //! //! Usage: //! cargo run -p metadata --release --example cold_audit -- ["Artist" ...] //! (no args = the built-in spread) use metadata::MetadataClient; use std::sync::Arc; use std::time::Instant; #[tokio::main] async fn main() { let mut artists: Vec = std::env::args().skip(1).collect(); if artists.is_empty() { artists = [ "Alvvays", // small, clean MB identity "Weezer", // medium, deep catalog "Queen", // huge, 1119 MB pressings "Death Grips", // mixtape/street edge "King Gizzard & The Lizard Wizard", // Last.fm mbid-index miss + '&' "Boards of Canada", // electronic, sparse Deezer "Duster", // namesake-collision shape ] .into_iter() .map(String::from) .collect(); } let mut bugs: Vec = Vec::new(); println!( "{:<36} {:>8} {:>8} {:>8} {:>6} {:>6} {:>7} {:>9} {:>8}", "artist", "overview", "page", "identity", "ov-alb", "pg-alb", "popular", "listeners", "dates" ); for artist in &artists { let client = Arc::new(MetadataClient::new()); let search = client.catalog_search(artist).await; let Some(found) = search .artists .iter() .find(|a| normalize(&a.name) == normalize(artist)) else { bugs.push(format!("{artist}: no exact-name artist in catalog search")); continue; }; let id = found.id; // The frontend shape: overview, page and the identity warm-up all in // flight together on one client. let (overview, page, identity) = tokio::join!( async { let t = Instant::now(); let overview = client.catalog_artist_overview(id).await; (t.elapsed().as_millis(), overview) }, async { let t = Instant::now(); let page = client.catalog_artist(id).await; (t.elapsed().as_millis(), page) }, async { let t = Instant::now(); let identity = client.catalog_artist_identity(id, artist).await; (t.elapsed().as_millis(), identity) }, ); let (ov_ms, overview) = overview; let (pg_ms, page) = page; let (id_ms, identity) = identity; let mut flag = |what: &str| bugs.push(format!("{artist}: {what}")); let (ov_albums, _ov_popular) = match &overview { Some(ov) => { if ov.artist.id != id { flag("overview echoed a different artist id"); } if ov.artist.name.is_empty() { flag("overview artist name empty"); } if ov.albums.is_empty() { flag("overview albums empty"); } if ov.popular_tracks.is_empty() { flag("overview popular empty"); } (ov.albums.len(), ov.popular_tracks.len()) } None => { flag("overview returned None"); (0, 0) } }; let (pg_albums, pg_popular, with_listeners, with_dates) = match &page { Some(pg) => { if pg.artist.id != id { flag("page echoed a different artist id"); } if pg.albums.is_empty() { flag("page albums empty"); } if pg.popular_tracks.is_empty() { flag("page popular empty"); } let listeners = pg .popular_tracks .iter() .filter(|t| t.listeners.is_some()) .count(); let dates = pg .albums .iter() .filter(|a| a.release_date.is_some()) .count(); // The page waits for real supplements; zero listener counts on // a popular row set means the Last.fm anchor died end-to-end. if !pg.popular_tracks.is_empty() && listeners == 0 { flag("page popular has zero Last.fm listener counts"); } (pg.albums.len(), pg.popular_tracks.len(), listeners, dates) } None => { flag("page returned None"); (0, 0, 0, 0) } }; if identity.artist_id != id { flag("identity echoed a different artist id"); } println!( "{:<36} {:>6}ms {:>6}ms {:>6}ms {:>6} {:>6} {:>7} {:>6}/{:<2} {:>4}/{:<3}", artist, ov_ms, pg_ms, id_ms, ov_albums, pg_albums, pg_popular, with_listeners, pg_popular, with_dates, pg_albums ); } println!(); if bugs.is_empty() { println!("NO INVARIANT FAILURES"); } else { println!("FAILURES ({}):", bugs.len()); for bug in &bugs { println!(" BUG {bug}"); } } } fn normalize(s: &str) -> String { s.to_lowercase() .chars() .filter(|c| c.is_alphanumeric()) .collect() }