use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time::Duration;
use dashmap::DashMap;
use crate::context::TransactionContext;
use crate::types::{TransactionId, TransactionInfo, TransactionMetrics, TransactionStats};
pub struct TransactionMonitor {
stats: Arc<TransactionStats>,
}
impl TransactionMonitor {
pub fn new(stats: Arc<TransactionStats>) -> Self {
Self { stats }
}
pub fn stats(&self) -> &TransactionStats {
&self.stats
}
pub fn get_metrics(
&self,
active_transactions: &DashMap<TransactionId, Arc<TransactionContext>>,
) -> TransactionMetrics {
let mut metrics = TransactionMetrics::new();
let durations: Vec<Duration> = active_transactions
.iter()
.map(|entry| entry.value().start_time.elapsed())
.collect();
metrics.total_count = self.stats.total_transactions.load(Ordering::Relaxed);
metrics.conflict_rate = self.stats.conflict_rate();
metrics.conflict_rate_windowed = self.stats.conflict_rate_windowed();
metrics.active_transactions = self.stats.active_transactions.load(Ordering::Relaxed);
metrics.committed_transactions = self.stats.committed_transactions.load(Ordering::Relaxed);
metrics.aborted_transactions = self.stats.aborted_transactions.load(Ordering::Relaxed);
metrics.timeout_transactions = self.stats.timeout_transactions.load(Ordering::Relaxed);
metrics.disconnect_transactions =
self.stats.disconnect_transactions.load(Ordering::Relaxed);
metrics.cleanup_failure_transactions = self
.stats
.cleanup_failure_transactions
.load(Ordering::Relaxed);
metrics.hook_dispatch_total = self.stats.hook_dispatch_total.load(Ordering::Relaxed);
metrics.hook_panic_total = self.stats.hook_panic_total.load(Ordering::Relaxed);
metrics.hook_slow_total = self.stats.hook_slow_total.load(Ordering::Relaxed);
metrics.active_statements = self.stats.active_statements.load(Ordering::Relaxed);
metrics.conflict_breakdown = self.stats.conflict_breakdown();
if !durations.is_empty() {
let mut sorted_durations = durations.clone();
sorted_durations.sort();
metrics.p50_duration = sorted_durations[sorted_durations.len() * 50 / 100];
metrics.p95_duration = sorted_durations[sorted_durations.len() * 95 / 100];
metrics.p99_duration = sorted_durations[sorted_durations.len() * 99 / 100];
let total: Duration = durations.iter().sum();
metrics.avg_duration = total / durations.len() as u32;
metrics.long_transactions = active_transactions
.iter()
.filter(|entry| entry.value().start_time.elapsed() > Duration::from_secs(10))
.map(|entry| entry.value().info())
.collect();
}
metrics
}
pub fn get_active_transactions(
&self,
active_transactions: &DashMap<TransactionId, Arc<TransactionContext>>,
) -> Vec<TransactionInfo> {
active_transactions
.iter()
.map(|entry| entry.value().info())
.collect()
}
pub fn get_long_transactions(
&self,
active_transactions: &DashMap<TransactionId, Arc<TransactionContext>>,
) -> Vec<TransactionInfo> {
active_transactions
.iter()
.filter(|entry| entry.value().start_time.elapsed() > Duration::from_secs(10))
.map(|entry| entry.value().info())
.collect()
}
pub fn get_transaction_info(
&self,
active_transactions: &DashMap<TransactionId, Arc<TransactionContext>>,
txn_id: TransactionId,
) -> Option<TransactionInfo> {
active_transactions
.get(&txn_id)
.map(|entry| entry.value().info())
}
pub fn list_active_transactions(
&self,
active_transactions: &DashMap<TransactionId, Arc<TransactionContext>>,
) -> Vec<TransactionInfo> {
active_transactions
.iter()
.filter(|entry| entry.value().state().can_execute())
.map(|entry| entry.value().info())
.collect()
}
pub fn list_transactions(
&self,
active_transactions: &DashMap<TransactionId, Arc<TransactionContext>>,
) -> Vec<TransactionInfo> {
active_transactions
.iter()
.map(|entry| entry.value().info())
.collect()
}
}
impl Default for TransactionMonitor {
fn default() -> Self {
Self::new(Arc::new(TransactionStats::new()))
}
}