//! Round-13 query-shape pack (needs the dev Postgres up; reads DATABASE_URL //! from `.env`). //! //! Three sections, each BEFORE (verbatim replica of the shipped query shape) //! vs AFTER (proposed shape), with equivalence/safety gates: //! //! [Q1] Group-notification recipient expansion — `get_users_by_ids`'s //! 21-column row (incl. the ≤512 KiB avatar `image` + `ui_preferences` //! JSONB) hydrated per member vs the notification-only projection //! (drops both heavy columns; the caller reads only email/eligibility //! fields). //! [Q2] Login provisioning idempotency — `list_calendars_by_owner(..) //! .is_empty()` / `get_address_books_by_owner(..).is_empty()` (hydrate //! every owned row) vs `SELECT EXISTS(...)`. //! [Q3] Recent-access recording — unconditional upsert + prune (2 //! round-trips) vs upsert-`RETURNING (xmax=0)` + prune-only-on-insert. //! //! Run: //! cargo run --release --features bench --example bench_round13_queries //! Tunables (env): BENCH_PASSES (200), BENCH_GROUP (30), BENCH_CALS (4), //! BENCH_RECENT_CAP (50) use std::env; use std::sync::Arc; use std::time::Instant; use sqlx::postgres::PgPoolOptions; use sqlx::{PgPool, Row}; use uuid::Uuid; fn env_or(key: &str, default: T) -> T { env::var(key) .ok() .and_then(|v| v.parse().ok()) .unwrap_or(default) } fn stats(mut s: Vec) -> (f64, f64, f64) { s.sort_by(|a, b| a.partial_cmp(b).unwrap()); let n = s.len(); ( s.iter().sum::() / n as f64, s[n / 2], s[((n as f64 * 0.95) as usize).min(n - 1)], ) } // ──────────────────────────────────────────────────────────────────────────── // [Q1] Notification recipient expansion — wide row vs narrow projection // ──────────────────────────────────────────────────────────────────────────── /// BEFORE, verbatim `get_users_by_ids` projection: 21 columns incl. `image` /// and `ui_preferences`. Touch the heavy columns like `User::from_data_full` /// does (materialize them) so the detoast/parse cost is counted. async fn recipients_before(pool: &PgPool, ids: &[Uuid]) -> Vec<(Uuid, String, bool)> { let rows = sqlx::query( r#" SELECT id, username, email, password_hash, role::text as role_text, storage_quota_bytes, storage_used_bytes, created_at, updated_at, last_login_at, active, oidc_provider, oidc_subject, image, is_external, given_name, family_name, email_verified_at, preferred_locale, notify_on_share, ui_preferences FROM auth.users WHERE id = ANY($1) "#, ) .bind(ids) .fetch_all(pool) .await .expect("recipients wide"); rows.into_iter() .map(|r| { let _image: Option = r.get("image"); let _prefs: serde_json::Value = r.get("ui_preferences"); (r.get("id"), r.get("email"), r.get("notify_on_share")) }) .collect() } /// AFTER: the shipped narrow projection (image + ui_preferences dropped). async fn recipients_after(pool: &PgPool, ids: &[Uuid]) -> Vec<(Uuid, String, bool)> { let rows = sqlx::query( r#" SELECT id, username, email, password_hash, role::text as role_text, storage_quota_bytes, storage_used_bytes, created_at, updated_at, last_login_at, active, oidc_provider, oidc_subject, is_external, given_name, family_name, email_verified_at, preferred_locale, notify_on_share FROM auth.users WHERE id = ANY($1) "#, ) .bind(ids) .fetch_all(pool) .await .expect("recipients narrow"); rows.into_iter() .map(|r| (r.get("id"), r.get("email"), r.get("notify_on_share"))) .collect() } async fn section_recipients(pool: &PgPool) { let group: usize = env_or("BENCH_GROUP", 30); let passes: usize = env_or("BENCH_PASSES", 200); // Seed a group of avatared users (256 KiB data-URI each). let mut ids = Vec::with_capacity(group); for i in 0..group { let id: Uuid = sqlx::query_scalar( "INSERT INTO auth.users (username, email, role, image, notify_on_share) VALUES ($1, $2, 'user', $3, true) RETURNING id", ) .bind(format!("bench13_rcpt_{i:04}")) .bind(format!("bench13_rcpt_{i:04}@bench.invalid")) .bind(format!( "data:image/png;base64,{}", "QUJDRA==".repeat(32 * 1024) )) .fetch_one(pool) .await .expect("seed recipient"); ids.push(id); } // Equivalence gate: same (id, email, notify) set either way. let mut b = recipients_before(pool, &ids).await; let mut a = recipients_after(pool, &ids).await; b.sort(); a.sort(); assert_eq!(b, a, "recipient projections differ"); assert_eq!(a.len(), group, "expected all members"); println!("# [Q1] gate: wide/narrow recipient sets identical ({group} members) — OK"); let mut wide = Vec::with_capacity(passes); for _ in 0..passes { let t = Instant::now(); std::hint::black_box(recipients_before(pool, &ids).await); wide.push(t.elapsed().as_secs_f64() * 1e3); } let mut narrow = Vec::with_capacity(passes); for _ in 0..passes { let t = Instant::now(); std::hint::black_box(recipients_after(pool, &ids).await); narrow.push(t.elapsed().as_secs_f64() * 1e3); } let (wm, wp50, wp95) = stats(wide); let (nm, np50, np95) = stats(narrow); println!("\n## [Q1] Group-notification recipient expansion ({group} avatared members)"); println!("| arm | mean ms | p50 ms | p95 ms |"); println!("| BEFORE wide row (incl. image) | {wm:>8.3} | {wp50:>7.3} | {wp95:>7.3} |"); println!("| AFTER narrow (email fields) | {nm:>8.3} | {np50:>7.3} | {np95:>7.3} |"); println!( "# {:.2}x faster, ~{} KiB avatar/ui_prefs off the wire per fan-out", wm / nm, group * 256 ); sqlx::query("DELETE FROM auth.users WHERE username LIKE 'bench13\\_rcpt\\_%'") .execute(pool) .await .expect("cleanup recipients"); if nm >= wm { eprintln!("GATE FAIL [Q1]: narrow not faster — rollback"); std::process::exit(1); } } // ──────────────────────────────────────────────────────────────────────────── // [Q2] Login provisioning idempotency — hydrate-all vs EXISTS // ──────────────────────────────────────────────────────────────────────────── async fn section_provisioning(pool: &PgPool) { let cals: usize = env_or("BENCH_CALS", 4); let passes: usize = env_or("BENCH_PASSES", 200); let owner: Uuid = sqlx::query_scalar( "INSERT INTO auth.users (username, email, role) VALUES ('bench13_prov', 'bench13_prov@bench.invalid', 'user') RETURNING id", ) .fetch_one(pool) .await .expect("seed owner"); for i in 0..cals { sqlx::query( "INSERT INTO caldav.calendars (id, name, owner_id, description, color) VALUES (gen_random_uuid(), $1, $2, $3, '#3b82f6')", ) .bind(format!("Cal {i}")) .bind(owner) .bind("A reasonably long calendar description to make the hydrated row wider") .execute(pool) .await .expect("seed calendar"); } async fn before_is_empty(pool: &PgPool, owner: Uuid) -> bool { // Verbatim: hydrate every owned calendar row, then `.is_empty()`. let rows = sqlx::query( "SELECT id, name, owner_id, description, color, is_public, created_at, updated_at FROM caldav.calendars WHERE owner_id = $1 ORDER BY name", ) .bind(owner) .fetch_all(pool) .await .expect("list calendars"); !rows.is_empty() } async fn after_exists(pool: &PgPool, owner: Uuid) -> bool { sqlx::query_scalar("SELECT EXISTS(SELECT 1 FROM caldav.calendars WHERE owner_id = $1)") .bind(owner) .fetch_one(pool) .await .expect("exists") } // Gate: identical verdict, present and absent. assert!(before_is_empty(pool, owner).await); assert!(after_exists(pool, owner).await); let ghost = Uuid::new_v4(); assert_eq!( before_is_empty(pool, ghost).await, after_exists(pool, ghost).await ); println!("# [Q2] gate: hydrate-all and EXISTS agree (present + absent) — OK"); let mut before = Vec::with_capacity(passes); for _ in 0..passes { let t = Instant::now(); std::hint::black_box(before_is_empty(pool, owner).await); before.push(t.elapsed().as_secs_f64() * 1e3); } let mut after = Vec::with_capacity(passes); for _ in 0..passes { let t = Instant::now(); std::hint::black_box(after_exists(pool, owner).await); after.push(t.elapsed().as_secs_f64() * 1e3); } let (bm, bp50, bp95) = stats(before); let (am, ap50, ap95) = stats(after); println!("\n## [Q2] Login provisioning idempotency probe ({cals} owned calendars)"); println!("| arm | mean ms | p50 ms | p95 ms |"); println!("| BEFORE list+hydrate .is_empty() | {bm:>7.3} | {bp50:>7.3} | {bp95:>7.3} |"); println!("| AFTER SELECT EXISTS | {am:>7.3} | {ap50:>7.3} | {ap95:>7.3} |"); println!( "# {:.2}x faster per login probe (×2: calendar + address book)", bm / am ); sqlx::query("DELETE FROM caldav.calendars WHERE owner_id = $1") .bind(owner) .execute(pool) .await .ok(); sqlx::query("DELETE FROM auth.users WHERE id = $1") .bind(owner) .execute(pool) .await .ok(); if am >= bm { eprintln!("GATE FAIL [Q2]: EXISTS not faster — rollback"); std::process::exit(1); } } // ──────────────────────────────────────────────────────────────────────────── // [Q3] Recent-access recording — upsert+prune (2 RTT) vs prune-on-insert // ──────────────────────────────────────────────────────────────────────────── async fn section_recent(pool: &PgPool) { let cap: i32 = env_or("BENCH_RECENT_CAP", 50); let passes: usize = env_or("BENCH_PASSES", 200); let user: Uuid = sqlx::query_scalar( "INSERT INTO auth.users (username, email, role) VALUES ('bench13_recent', 'bench13_recent@bench.invalid', 'user') RETURNING id", ) .fetch_one(pool) .await .expect("seed recent user"); async fn upsert_before(pool: &PgPool, user: Uuid, item: &str) { sqlx::query( "INSERT INTO auth.user_recent_files (user_id, item_id, item_type, accessed_at) VALUES ($1, $2, 'file', CURRENT_TIMESTAMP) ON CONFLICT (user_id, item_id, item_type) DO UPDATE SET accessed_at = CURRENT_TIMESTAMP", ) .bind(user) .bind(item) .execute(pool) .await .expect("upsert"); } async fn prune(pool: &PgPool, user: Uuid, cap: i32) { sqlx::query( "DELETE FROM auth.user_recent_files WHERE id IN (SELECT id FROM auth.user_recent_files WHERE user_id = $1 ORDER BY accessed_at DESC OFFSET $2)", ) .bind(user) .bind(cap) .execute(pool) .await .expect("prune"); } async fn upsert_after(pool: &PgPool, user: Uuid, item: &str) -> bool { sqlx::query_scalar( "INSERT INTO auth.user_recent_files (user_id, item_id, item_type, accessed_at) VALUES ($1, $2, 'file', CURRENT_TIMESTAMP) ON CONFLICT (user_id, item_id, item_type) DO UPDATE SET accessed_at = CURRENT_TIMESTAMP RETURNING (xmax = 0)", ) .bind(user) .bind(item) .fetch_one(pool) .await .expect("upsert returning") } // Fill to the cap so the set is at steady state. for i in 0..cap { upsert_before(pool, user, &format!("seed-{i:04}")).await; } // Gate: the AFTER path must keep the row count at the cap AND flag // insert-vs-update correctly. Re-access an existing item → update (no // prune); a brand-new item → insert (prune keeps count == cap). let existing = "seed-0000"; assert!( !upsert_after(pool, user, existing).await, "re-access must be an UPDATE" ); let fresh = "gate-new-item"; assert!( upsert_after(pool, user, fresh).await, "new item must be an INSERT" ); prune(pool, user, cap).await; let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM auth.user_recent_files WHERE user_id = $1") .bind(user) .fetch_one(pool) .await .unwrap(); assert_eq!(count, cap as i64, "prune-on-insert keeps the cap"); println!("# [Q3] gate: xmax flags insert/update, count stays at cap — OK"); // BEFORE: every record = upsert + prune (2 round-trips). Model the // common case — re-accessing items already in the set (all UPDATEs). let mut before = Vec::with_capacity(passes); for i in 0..passes { let item = format!("seed-{:04}", i % cap as usize); let t = Instant::now(); upsert_before(pool, user, &item).await; prune(pool, user, cap).await; before.push(t.elapsed().as_secs_f64() * 1e3); } // AFTER: upsert RETURNING; prune only when inserted (never, here). let mut after = Vec::with_capacity(passes); for i in 0..passes { let item = format!("seed-{:04}", i % cap as usize); let t = Instant::now(); let inserted = upsert_after(pool, user, &item).await; if inserted { prune(pool, user, cap).await; } after.push(t.elapsed().as_secs_f64() * 1e3); } let (bm, bp50, bp95) = stats(before); let (am, ap50, ap95) = stats(after); println!("\n## [Q3] Recent-access recording (re-access = UPDATE, common path)"); println!("| arm | mean ms | p50 ms | p95 ms |"); println!("| BEFORE upsert + prune (2 RTT) | {bm:>7.3} | {bp50:>7.3} | {bp95:>7.3} |"); println!("| AFTER upsert; prune-on-insert | {am:>7.3} | {ap50:>7.3} | {ap95:>7.3} |"); println!( "# {:.2}x faster on re-access; prune round-trip skipped", bm / am ); sqlx::query("DELETE FROM auth.user_recent_files WHERE user_id = $1") .bind(user) .execute(pool) .await .ok(); sqlx::query("DELETE FROM auth.users WHERE id = $1") .bind(user) .execute(pool) .await .ok(); if am >= bm { eprintln!("GATE FAIL [Q3]: prune-on-insert not faster — rollback"); std::process::exit(1); } } #[tokio::main(flavor = "multi_thread", worker_threads = 4)] async fn main() { let _ = dotenvy::dotenv(); let url = std::env::var("DATABASE_URL").expect("DATABASE_URL required (see .env)"); let pool = Arc::new( PgPoolOptions::new() .max_connections(8) .connect(&url) .await .expect("connect"), ); println!("#################################################################"); println!("# Round-13 query-shape pack"); println!("#################################################################"); section_recipients(&pool).await; section_provisioning(&pool).await; section_recent(&pool).await; println!("\nGATE PASS (all sections)"); }