Files
Oxicloud/examples/bench_round13_queries.rs
T
Claude f58d72a780 perf: round 13 — grouped-view virtualization, notification/login query narrowing, HTTP dedup, locale precompute
Benchmark-gated (BEFORE/AFTER + equivalence/safety gate per change), same
discipline as rounds 2-12. Full write-up in benches/ROUND13.md.

Shipped:
- V1 Grouped views windowed (files route + ResourceList). The grid arm was
  the last unwindowed path (trash is grouped-by-default in grid): each
  swimlane now feeds its own VirtualList, outer container a flex stack.
  vitest gate: 800-item grouped grid mounts <120 .file-item (was 800).
- Q1 get_users_by_ids drops the <=512 KiB avatar image + ui_preferences
  JSONB (notification path never reads them). 30-member fan-out 8.60 ->
  0.25 ms (34.3x), ~7.7 MB off the wire.
- Q2 Login provisioning is_empty() -> SELECT EXISTS for calendar + address
  book (every login). 0.193 -> 0.170 ms, widens with owned-row count.
- Q3 Recent-access prunes only when the upsert inserted (RETURNING xmax=0)
  — a re-access can't grow the set. 0.567 -> 0.324 ms (1.75x).
- L1 Locale supported-codes precomputed once vs rebuilt per anonymous
  request. 616 -> 17.3 ns (35.7x), 18 -> 1 allocs.
- H1 Duplicate /api TraceLayer removed (global stack already wraps it).
  1.86 -> 1.42 us/request, -6 allocs.
- H2 client_ip span field: borrow-only ClientIpDisplay vs owned String.
  187 -> 173 ns, -1 alloc.

Not shipped (discipline): the "media hooks read the blob 3x" lead was a
correctness bug, not a perf dup — the raw-path metadata/faces readers
resolve only for local+unencrypted+single-chunk blobs and silently produce
nothing otherwise. Flagged for maintainers; routing through read_blob_bytes
is a correctness fix (perf-neutral-to-negative), not a benchmark-gated
perf change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BfidAJD5AHw23jtvBUNamB
2026-07-19 08:17:48 +00:00

422 lines
16 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 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<T: std::str::FromStr>(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, f64) {
s.sort_by(|a, b| a.partial_cmp(b).unwrap());
let n = s.len();
(
s.iter().sum::<f64>() / 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<String> = 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)");
}