use criterion::{criterion_group, criterion_main, Bencher, Criterion}; use sqlx::PgPool; use sqlx::postgres::PgPoolOptions; use std::time::{Duration, Instant}; fn bench_pgpool_acquire(c: &mut Criterion) { let mut group = c.benchmark_group("bench_pgpool_acquire"); for &concurrent in [5u32, 10, 50, 100, 500, 1000, 5000 /*, 10_000, 50_000*/].iter() { for &fair in [false, true].iter() { let fairness = if fair { "(fair)" } else { "(unfair)" }; group.bench_with_input( format!("{concurrent} concurrent {fairness}"), &(concurrent, fair), |b, &(concurrent, fair)| do_bench_acquire(b, concurrent, fair), ); } } group.finish(); } fn do_bench_acquire(b: &mut Bencher, concurrent: u32, fair: bool) { let pool = sqlx::__rt::block_on( PgPoolOptions::new() // we don't want timeouts because we want to see how the pool degrades .acquire_timeout(Duration::from_secs(3600)) // force the pool to start full .min_connections(50) .max_connections(50) // we're not benchmarking `ping()` .test_before_acquire(false) .__fair(fair) .connect( &dotenvy::var("DATABASE_URL").expect("DATABASE_URL must be set to run benchmarks"), ), ) .expect("failed to open PgPool"); for _ in 0..concurrent { let pool = pool.clone(); sqlx::__rt::enter_runtime(|| { sqlx::__rt::spawn(async move { while !pool.is_closed() { let conn = match pool.acquire().await { Ok(conn) => conn, Err(sqlx::Error::PoolClosed) => break, Err(e) => panic!("failed to acquire concurrent connection: {e}"), }; // pretend we're using the connection sqlx::__rt::sleep(Duration::from_micros(500)).await; drop(criterion::black_box(conn)); } }) }); } b.iter_custom(|iters| { sqlx::__rt::block_on(async { // take the start time inside the future to make sure we only count once it's running let start = Instant::now(); for _ in 0..iters { criterion::black_box( pool.acquire() .await .expect("failed to acquire connection for benchmark"), ); } start.elapsed() }) }); sqlx::__rt::block_on(pool.close()); } criterion_group!(pg_pool, bench_pgpool_acquire); criterion_main!(pg_pool);