use std::cell::RefCell;
use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
static ENABLED: AtomicBool = AtomicBool::new(false);
static THRESHOLD: AtomicUsize = AtomicUsize::new(10);
pub fn enable(threshold: usize) {
THRESHOLD.store(threshold.max(1), Ordering::Relaxed);
ENABLED.store(true, Ordering::Relaxed);
}
pub fn disable() {
ENABLED.store(false, Ordering::Relaxed);
}
pub fn is_enabled() -> bool {
ENABLED.load(Ordering::Relaxed)
}
#[cfg(feature = "postgres")]
#[allow(dead_code)] fn threshold() -> usize {
THRESHOLD.load(Ordering::Relaxed)
}
thread_local! {
static COUNTERS: RefCell<HashMap<String, usize>> = RefCell::new(HashMap::new());
}
pub fn reset() {
COUNTERS.with(|c| c.borrow_mut().clear());
}
#[cfg(feature = "postgres")]
#[allow(dead_code)] pub(crate) fn record(table: &str, result_count: usize) {
if !is_enabled() {
return;
}
let count = COUNTERS.with(|c| {
let mut map = c.borrow_mut();
let entry = map.entry(table.to_string()).or_insert(0);
*entry += 1;
*entry
});
if count == threshold() + 1 {
eprintln!(
"[rok-fluent] \u{26a0} N+1 query detected\n \
Table `{table}` has been queried {count} times in this request scope.\n \
If loading a relation, add .with(\"{table}\") for batch loading.\n \
Last result: {result_count} row(s)."
);
}
}
pub fn auto_configure() {
if let Ok(t) = std::env::var("N1_THRESHOLD") {
if let Ok(n) = t.parse::<usize>() {
enable(n);
return;
}
}
let is_dev = matches!(
std::env::var("APP_ENV").as_deref(),
Ok("development" | "dev" | "local")
);
if is_dev {
enable(10);
}
}