feat: add tracing-flame to benches/any-pool

This commit is contained in:
Austin Bonander 2026-01-02 16:13:30 -08:00
parent 009564490b
commit 4b25ab8dce
5 changed files with 115 additions and 29 deletions

45
Cargo.lock generated
View File

@ -2193,6 +2193,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "matchers"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
dependencies = [
"regex-automata",
]
[[package]]
name = "matchit"
version = "0.5.0"
@ -2343,12 +2352,11 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"overload",
"winapi",
"windows-sys 0.60.2",
]
[[package]]
@ -2495,12 +2503,6 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "owo-colors"
version = "4.2.2"
@ -3492,7 +3494,6 @@ dependencies = [
"async-std",
"criterion",
"dotenvy",
"env_logger",
"futures-util",
"hex",
"libsqlite3-sys",
@ -3511,6 +3512,8 @@ dependencies = [
"time",
"tokio",
"tracing",
"tracing-flame",
"tracing-subscriber",
"trybuild",
"url",
]
@ -4005,6 +4008,7 @@ dependencies = [
"dotenvy",
"env_logger",
"sqlx",
"tracing-subscriber",
]
[[package]]
@ -4451,6 +4455,17 @@ dependencies = [
"tracing-subscriber",
]
[[package]]
name = "tracing-flame"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bae117ee14789185e129aaee5d93750abe67fdc5a9a62650452bfe4e122a3a9"
dependencies = [
"lazy_static",
"tracing",
"tracing-subscriber",
]
[[package]]
name = "tracing-log"
version = "0.2.0"
@ -4464,14 +4479,18 @@ dependencies = [
[[package]]
name = "tracing-subscriber"
version = "0.3.19"
version = "0.3.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
dependencies = [
"matchers",
"nu-ansi-term",
"once_cell",
"regex-automata",
"sharded-slab",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
"tracing-log",
]

View File

@ -241,6 +241,7 @@ criterion = { version = "0.7.0", features = ["async_tokio"] }
libsqlite3-sys = { version = "0.30.1" }
tracing = "0.1.41"
tracing-flame = "0.2.0"
tracing-subscriber = "0.3.20"
# If this is an unconditional dev-dependency then Cargo will *always* try to build `libsqlite3-sys`,

View File

@ -4,6 +4,10 @@ use std::fmt::{Display, Formatter};
use std::thread;
use std::time::{Duration, Instant};
use tracing::Instrument;
use tracing_flame::FlameLayer;
use tracing_subscriber::{EnvFilter, Layer};
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
#[derive(Debug)]
struct Input {
@ -24,7 +28,27 @@ impl Display for Input {
fn bench_pool(c: &mut Criterion) {
sqlx::any::install_default_drivers();
tracing_subscriber::fmt::try_init().ok();
let _guard = if let Ok(path) = dotenvy::var("FLAMEGRAPH_OUT") {
let (layer, guard) = FlameLayer::with_file(&path)
.expect(&format!("error opening path {path:?} (`FLAMEGRAPH_OUT`)"));
tracing_subscriber::registry()
.with(
tracing_subscriber::fmt::layer().with_filter(EnvFilter::from_default_env())
)
.with(layer.with_threads_collapsed(true))
.try_init()
.ok();
tracing::info!("Writing flamegraph to {path:?}");
Some(guard)
} else {
tracing_subscriber::fmt::try_init().ok();
None
};
let database_url = dotenvy::var("DATABASE_URL").expect("DATABASE_URL must be set");
@ -136,4 +160,4 @@ fn bench_pool_with(b: &mut Bencher, input: &Input, database_url: &str) {
}
criterion_group!(benches, bench_pool,);
criterion_main!(benches);
criterion_main!(benches);

View File

@ -13,6 +13,7 @@ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc;
use std::task::Poll;
use std::time::Duration;
use tracing::Instrument;
pub struct ConnectionSet<C> {
global: Arc<Global>,
@ -120,22 +121,37 @@ impl<C> ConnectionSet<C> {
}
async fn acquire_inner(&self, pref: AcquirePreference) -> SlotGuard<C> {
let preferred_slot = current_thread_id() % self.slots.len();
tracing::trace!(preferred_slot, ?pref, "acquire_inner");
let preferred_slot = current_thread_id() % self.slots.len();
// Always try to lock the connection associated with our thread ID
let mut acquire_preferred = pin!(self.slots[preferred_slot].acquire(pref));
let alternate_slot = (preferred_slot + 547usize.wrapping_mul(
Arc::strong_count(&self.slots[preferred_slot].connection)
)) % self.slots.len();
let mut acquire_alternate = pin!(self.slots[alternate_slot].acquire(pref));
let mut listen_global = pin!(self.global.listen(pref));
let mut yielded = false;
let mut yielded_1 = false;
let mut yielded_2 = false;
std::future::poll_fn(|cx| {
if let Poll::Ready(locked) = acquire_preferred.as_mut().poll(cx) {
return Poll::Ready(locked);
}
if let Poll::Ready(locked) = acquire_alternate.as_mut().poll(cx) {
return Poll::Ready(locked);
}
// if !yielded_1 {
// cx.waker().wake_by_ref();
// yielded_1 = true;
// return Poll::Pending;
// }
if let Poll::Ready(slot) = listen_global.as_mut().poll(cx) {
if let Some(locked) = self.slots[slot].try_acquire(pref) {
return Poll::Ready(locked);
@ -144,9 +160,9 @@ impl<C> ConnectionSet<C> {
listen_global.as_mut().set(self.global.listen(pref));
}
if !yielded {
if !yielded_2 {
cx.waker().wake_by_ref();
yielded = true;
yielded_2 = true;
return Poll::Pending;
}
@ -156,6 +172,12 @@ impl<C> ConnectionSet<C> {
Poll::Pending
})
.instrument(tracing::trace_span!(
target: "sqlx::pool::connection_set",
"acquire_inner",
preferred_slot,
?pref,
))
.await
}
@ -174,14 +196,24 @@ impl<C> ConnectionSet<C> {
}
fn try_acquire(&self, pref: AcquirePreference) -> Option<SlotGuard<C>> {
let mut search_slot = current_thread_id() % self.slots.len();
let preferred_slot = current_thread_id() % self.slots.len();
for _ in 0..self.slots.len() {
if let Some(locked) = self.slots[search_slot].try_acquire(pref) {
return Some(locked);
let (slots_before, slots_after) = self.slots.split_at(preferred_slot);
let (preferred_slot, slots_after) = slots_after.split_first().unwrap();
if let Some(locked) = preferred_slot.try_acquire(pref) {
return Some(locked);
}
for slot in slots_before.iter().chain(slots_after).rev() {
if self.global.locked_set[slot.index].load(Ordering::Relaxed) {
continue;
}
search_slot = self.next_slot(search_slot);
if let Some(locked) = slot.try_acquire(pref) {
return Some(locked);
}
}
None
@ -398,6 +430,7 @@ impl<C> SlotGuard<C> {
let connected = locked.is_some();
self.slot.set_is_connected(connected);
self.slot.locked.store(false, Ordering::Release);
self.slot.global.locked_set[self.slot.index].store(false, Ordering::Relaxed);
connected
})
}

View File

@ -20,7 +20,7 @@ use crate::{private_tracing_dynamic_event, rt};
use event_listener::listener;
use futures_util::future::{self};
use std::time::{Duration, Instant};
use tracing::Level;
use tracing::{Instrument, Level};
const GRACEFUL_CLOSE_TIMEOUT: Duration = Duration::from_secs(5);
const TEST_BEFORE_ACQUIRE_TIMEOUT: Duration = Duration::from_secs(60);
@ -181,7 +181,7 @@ impl<DB: Database> PoolInner<DB> {
tracing::trace!("waiting for any connection");
let disconnected = match self.connections.acquire_any().await {
Ok(conn) => match finish_acquire(self, conn).await {
Ok(conn) => match self.finish_acquire(conn).await {
Ok(conn) => return Ok(conn),
Err(slot) => slot,
},
@ -199,7 +199,7 @@ impl<DB: Database> PoolInner<DB> {
match race(&mut connect_task, self.connections.acquire_connected()).await {
Ok(Ok(conn)) => return Ok(conn),
Ok(Err(e)) => return Err(e),
Err(conn) => match finish_acquire(self, conn).await {
Err(conn) => match self.finish_acquire(conn).await {
Ok(conn) => return Ok(conn),
Err(_) => continue,
},
@ -207,6 +207,15 @@ impl<DB: Database> PoolInner<DB> {
}
}
#[inline(always)]
async fn finish_acquire(self: &Arc<Self>, conn: ConnectedSlot<ConnectionInner<DB>>) -> Result<PoolConnection<DB>, DisconnectedSlot<ConnectionInner<DB>>> {
let span = tracing::trace_span!(target: "sqlx::pool", "finish_acquire", connection_id=?conn.id);
finish_acquire(self, conn)
.instrument(span)
.await
}
pub(crate) async fn try_min_connections(
self: &Arc<Self>,
deadline: Option<Instant>,