use serde::Serialize;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Duration;
#[derive(Debug, Clone, Copy, Default, Serialize, PartialEq, Eq)]
pub struct RuntimeProfileSnapshot {
pub wait_calls: u64,
pub wait_nanos: u64,
pub ddl_shared_waits: u64,
pub ddl_exclusive_waits: u64,
pub visibility_shared_waits: u64,
pub visibility_exclusive_waits: u64,
pub seal_shared_waits: u64,
pub membership_exclusive_waits: u64,
pub checkpoint_mutex_waits: u64,
pub hash_build_calls: u64,
pub hash_build_rows: u64,
pub hash_build_nanos: u64,
pub index_lookup_calls: u64,
pub index_lookup_keys: u64,
pub index_lookup_hits: u64,
pub index_lookup_nanos: u64,
pub protocol_round_trips: u64,
pub protocol_round_trip_nanos: u64,
}
impl RuntimeProfileSnapshot {
pub fn delta(self, before: Self) -> Self {
macro_rules! delta {
($field:ident) => {
self.$field.saturating_sub(before.$field)
};
}
Self {
wait_calls: delta!(wait_calls),
wait_nanos: delta!(wait_nanos),
ddl_shared_waits: delta!(ddl_shared_waits),
ddl_exclusive_waits: delta!(ddl_exclusive_waits),
visibility_shared_waits: delta!(visibility_shared_waits),
visibility_exclusive_waits: delta!(visibility_exclusive_waits),
seal_shared_waits: delta!(seal_shared_waits),
membership_exclusive_waits: delta!(membership_exclusive_waits),
checkpoint_mutex_waits: delta!(checkpoint_mutex_waits),
hash_build_calls: delta!(hash_build_calls),
hash_build_rows: delta!(hash_build_rows),
hash_build_nanos: delta!(hash_build_nanos),
index_lookup_calls: delta!(index_lookup_calls),
index_lookup_keys: delta!(index_lookup_keys),
index_lookup_hits: delta!(index_lookup_hits),
index_lookup_nanos: delta!(index_lookup_nanos),
protocol_round_trips: delta!(protocol_round_trips),
protocol_round_trip_nanos: delta!(protocol_round_trip_nanos),
}
}
}
struct RuntimeProfileCounters {
wait_calls: AtomicU64,
wait_nanos: AtomicU64,
ddl_shared_waits: AtomicU64,
ddl_exclusive_waits: AtomicU64,
visibility_shared_waits: AtomicU64,
visibility_exclusive_waits: AtomicU64,
seal_shared_waits: AtomicU64,
membership_exclusive_waits: AtomicU64,
checkpoint_mutex_waits: AtomicU64,
hash_build_calls: AtomicU64,
hash_build_rows: AtomicU64,
hash_build_nanos: AtomicU64,
index_lookup_calls: AtomicU64,
index_lookup_keys: AtomicU64,
index_lookup_hits: AtomicU64,
index_lookup_nanos: AtomicU64,
protocol_round_trips: AtomicU64,
protocol_round_trip_nanos: AtomicU64,
}
impl RuntimeProfileCounters {
const fn new() -> Self {
Self {
wait_calls: AtomicU64::new(0),
wait_nanos: AtomicU64::new(0),
ddl_shared_waits: AtomicU64::new(0),
ddl_exclusive_waits: AtomicU64::new(0),
visibility_shared_waits: AtomicU64::new(0),
visibility_exclusive_waits: AtomicU64::new(0),
seal_shared_waits: AtomicU64::new(0),
membership_exclusive_waits: AtomicU64::new(0),
checkpoint_mutex_waits: AtomicU64::new(0),
hash_build_calls: AtomicU64::new(0),
hash_build_rows: AtomicU64::new(0),
hash_build_nanos: AtomicU64::new(0),
index_lookup_calls: AtomicU64::new(0),
index_lookup_keys: AtomicU64::new(0),
index_lookup_hits: AtomicU64::new(0),
index_lookup_nanos: AtomicU64::new(0),
protocol_round_trips: AtomicU64::new(0),
protocol_round_trip_nanos: AtomicU64::new(0),
}
}
fn reset(&self) {
self.wait_calls.store(0, Ordering::Relaxed);
self.wait_nanos.store(0, Ordering::Relaxed);
self.ddl_shared_waits.store(0, Ordering::Relaxed);
self.ddl_exclusive_waits.store(0, Ordering::Relaxed);
self.visibility_shared_waits.store(0, Ordering::Relaxed);
self.visibility_exclusive_waits.store(0, Ordering::Relaxed);
self.seal_shared_waits.store(0, Ordering::Relaxed);
self.membership_exclusive_waits.store(0, Ordering::Relaxed);
self.checkpoint_mutex_waits.store(0, Ordering::Relaxed);
self.hash_build_calls.store(0, Ordering::Relaxed);
self.hash_build_rows.store(0, Ordering::Relaxed);
self.hash_build_nanos.store(0, Ordering::Relaxed);
self.index_lookup_calls.store(0, Ordering::Relaxed);
self.index_lookup_keys.store(0, Ordering::Relaxed);
self.index_lookup_hits.store(0, Ordering::Relaxed);
self.index_lookup_nanos.store(0, Ordering::Relaxed);
self.protocol_round_trips.store(0, Ordering::Relaxed);
self.protocol_round_trip_nanos.store(0, Ordering::Relaxed);
}
fn snapshot(&self) -> RuntimeProfileSnapshot {
RuntimeProfileSnapshot {
wait_calls: self.wait_calls.load(Ordering::Relaxed),
wait_nanos: self.wait_nanos.load(Ordering::Relaxed),
ddl_shared_waits: self.ddl_shared_waits.load(Ordering::Relaxed),
ddl_exclusive_waits: self.ddl_exclusive_waits.load(Ordering::Relaxed),
visibility_shared_waits: self.visibility_shared_waits.load(Ordering::Relaxed),
visibility_exclusive_waits: self.visibility_exclusive_waits.load(Ordering::Relaxed),
seal_shared_waits: self.seal_shared_waits.load(Ordering::Relaxed),
membership_exclusive_waits: self.membership_exclusive_waits.load(Ordering::Relaxed),
checkpoint_mutex_waits: self.checkpoint_mutex_waits.load(Ordering::Relaxed),
hash_build_calls: self.hash_build_calls.load(Ordering::Relaxed),
hash_build_rows: self.hash_build_rows.load(Ordering::Relaxed),
hash_build_nanos: self.hash_build_nanos.load(Ordering::Relaxed),
index_lookup_calls: self.index_lookup_calls.load(Ordering::Relaxed),
index_lookup_keys: self.index_lookup_keys.load(Ordering::Relaxed),
index_lookup_hits: self.index_lookup_hits.load(Ordering::Relaxed),
index_lookup_nanos: self.index_lookup_nanos.load(Ordering::Relaxed),
protocol_round_trips: self.protocol_round_trips.load(Ordering::Relaxed),
protocol_round_trip_nanos: self.protocol_round_trip_nanos.load(Ordering::Relaxed),
}
}
}
static RUNTIME_PROFILE: RuntimeProfileCounters = RuntimeProfileCounters::new();
pub fn reset_runtime_profile() {
RUNTIME_PROFILE.reset();
}
pub fn runtime_profile_snapshot() -> RuntimeProfileSnapshot {
RUNTIME_PROFILE.snapshot()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RuntimeWaitKind {
DdlShared,
DdlExclusive,
VisibilityShared,
VisibilityExclusive,
SealShared,
MembershipExclusive,
CheckpointMutex,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MetadataPkCountFallback {
Snapshot,
SealOverlap,
Unsupported,
CandidateLimit,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProtocolColumnBatchFallback {
RowState,
QueryShape,
StorageShape,
Schema,
Unknown,
}
#[inline]
pub fn record_runtime_wait(kind: RuntimeWaitKind, elapsed: Duration) {
#[cfg(feature = "bench-harness")]
{
add(&RUNTIME_PROFILE.wait_calls, 1);
add(&RUNTIME_PROFILE.wait_nanos, duration_nanos(elapsed));
let counter = match kind {
RuntimeWaitKind::DdlShared => &RUNTIME_PROFILE.ddl_shared_waits,
RuntimeWaitKind::DdlExclusive => &RUNTIME_PROFILE.ddl_exclusive_waits,
RuntimeWaitKind::VisibilityShared => &RUNTIME_PROFILE.visibility_shared_waits,
RuntimeWaitKind::VisibilityExclusive => &RUNTIME_PROFILE.visibility_exclusive_waits,
RuntimeWaitKind::SealShared => &RUNTIME_PROFILE.seal_shared_waits,
RuntimeWaitKind::MembershipExclusive => &RUNTIME_PROFILE.membership_exclusive_waits,
RuntimeWaitKind::CheckpointMutex => &RUNTIME_PROFILE.checkpoint_mutex_waits,
};
add(counter, 1);
}
#[cfg(not(feature = "bench-harness"))]
let _ = (kind, elapsed);
}
#[inline]
pub fn record_hash_build(rows: usize, elapsed: Duration) {
#[cfg(feature = "bench-harness")]
{
add(&RUNTIME_PROFILE.hash_build_calls, 1);
add(&RUNTIME_PROFILE.hash_build_rows, rows as u64);
add(&RUNTIME_PROFILE.hash_build_nanos, duration_nanos(elapsed));
}
#[cfg(not(feature = "bench-harness"))]
let _ = (rows, elapsed);
}
#[inline]
pub fn record_index_lookup(keys: usize, hits: usize, elapsed: Duration) {
#[cfg(feature = "bench-harness")]
{
add(&RUNTIME_PROFILE.index_lookup_calls, 1);
add(&RUNTIME_PROFILE.index_lookup_keys, keys as u64);
add(&RUNTIME_PROFILE.index_lookup_hits, hits as u64);
add(&RUNTIME_PROFILE.index_lookup_nanos, duration_nanos(elapsed));
}
#[cfg(not(feature = "bench-harness"))]
let _ = (keys, hits, elapsed);
}
#[inline]
pub fn record_protocol_round_trip(elapsed: Duration) {
#[cfg(feature = "bench-harness")]
{
add(&RUNTIME_PROFILE.protocol_round_trips, 1);
add(
&RUNTIME_PROFILE.protocol_round_trip_nanos,
duration_nanos(elapsed),
);
}
#[cfg(not(feature = "bench-harness"))]
let _ = elapsed;
}
#[inline]
#[cfg(feature = "bench-harness")]
fn add(counter: &AtomicU64, value: u64) {
counter.fetch_add(value, Ordering::Relaxed);
}
#[inline]
#[cfg(feature = "bench-harness")]
fn duration_nanos(elapsed: Duration) -> u64 {
elapsed.as_nanos().min(u128::from(u64::MAX)) as u64
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn runtime_profile_delta_saturates_each_counter() {
let before = RuntimeProfileSnapshot {
index_lookup_calls: 7,
index_lookup_keys: 11,
..RuntimeProfileSnapshot::default()
};
let after = RuntimeProfileSnapshot {
index_lookup_calls: 9,
index_lookup_keys: 5,
..RuntimeProfileSnapshot::default()
};
let delta = after.delta(before);
assert_eq!(delta.index_lookup_calls, 2);
assert_eq!(delta.index_lookup_keys, 0);
}
}