use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::time::{SystemTime, Instant, Duration};
#[derive(Debug, Clone, Default)]
pub struct BasicStats {
pub success_count: u64,
pub failure_count: u64,
pub last_success: Option<SystemTime>,
pub last_failure: Option<SystemTime>,
pub avg_response_time: u64,
}
type TransportStats = BasicStats;
#[derive(Debug)]
pub struct UpstreamMonitor {
stats: Arc<RwLock<HashMap<String, DetailedStats>>>,
check_interval: Duration,
config: UpstreamConfig,
}
#[derive(Debug, Clone)]
pub struct UpstreamConfig {
pub min_success_rate: f64,
pub max_avg_response_time: Duration,
pub max_consecutive_failures: u32,
pub recovery_success_count: u32,
pub stats_window_size: usize,
pub max_unavailable_duration: Duration,
}
#[derive(Debug, Clone, PartialEq)]
pub enum UpstreamStatus {
Available,
Unavailable,
Unknown,
}
#[derive(Debug, Clone)]
pub struct DetailedStats {
pub success_count: u64,
pub failure_count: u64,
pub last_success: Option<SystemTime>,
pub last_failure: Option<SystemTime>,
pub avg_response_time: u64,
pub consecutive_failures: u32,
pub consecutive_successes: u32,
pub upstream_status: UpstreamStatus,
pub status_changed_at: SystemTime,
}
impl Default for DetailedStats {
fn default() -> Self {
Self {
success_count: 0,
failure_count: 0,
last_success: None,
last_failure: None,
avg_response_time: 0,
consecutive_failures: 0,
consecutive_successes: 0,
upstream_status: UpstreamStatus::Unknown,
status_changed_at: SystemTime::now(),
}
}
}
impl UpstreamMonitor {
pub fn with_config(check_interval: Duration, config: UpstreamConfig) -> Self {
Self {
stats: Arc::new(RwLock::new(HashMap::new())),
check_interval,
config,
}
}
pub fn record_success(&self, transport_type: &str, duration: Duration) {
if let Ok(mut stats) = self.stats.write() {
let detailed_stats = stats.entry(transport_type.to_string())
.or_insert_with(DetailedStats::default);
detailed_stats.success_count += 1;
detailed_stats.last_success = Some(SystemTime::now());
if detailed_stats.avg_response_time == 0 {
detailed_stats.avg_response_time = duration.as_millis() as u64;
} else {
detailed_stats.avg_response_time =
(detailed_stats.avg_response_time * 9 + duration.as_millis() as u64) / 10;
}
detailed_stats.consecutive_failures = 0;
detailed_stats.consecutive_successes += 1;
self.update_upstream_status(detailed_stats);
}
}
pub fn record_failure(&self, transport_type: &str) {
if let Ok(mut stats) = self.stats.write() {
let detailed_stats = stats.entry(transport_type.to_string())
.or_insert_with(DetailedStats::default);
detailed_stats.failure_count += 1;
detailed_stats.last_failure = Some(SystemTime::now());
detailed_stats.consecutive_successes = 0;
detailed_stats.consecutive_failures += 1;
self.update_upstream_status(detailed_stats);
}
}
fn update_upstream_status(&self, stats: &mut DetailedStats) {
let old_status = stats.upstream_status.clone();
let mut new_status = UpstreamStatus::Unknown;
let total = stats.success_count + stats.failure_count;
if total < 3 {
new_status = UpstreamStatus::Unknown;
} else {
new_status = UpstreamStatus::Available;
if stats.consecutive_failures >= self.config.max_consecutive_failures {
new_status = UpstreamStatus::Unavailable;
}
if total >= 5 && (stats.success_count as f64 / total as f64) < self.config.min_success_rate {
new_status = UpstreamStatus::Unavailable;
}
if stats.avg_response_time > 0 && Duration::from_millis(stats.avg_response_time) > self.config.max_avg_response_time {
new_status = UpstreamStatus::Unavailable;
}
}
if stats.upstream_status == UpstreamStatus::Unavailable {
if stats.consecutive_successes >= self.config.recovery_success_count {
new_status = UpstreamStatus::Available;
} else {
new_status = UpstreamStatus::Unavailable;
}
}
if stats.upstream_status == UpstreamStatus::Unavailable {
if let Ok(elapsed) = stats.status_changed_at.elapsed() {
if elapsed > self.config.max_unavailable_duration {
if stats.consecutive_successes > 0 {
new_status = UpstreamStatus::Available;
}
}
}
}
if new_status != old_status {
stats.upstream_status = new_status;
stats.status_changed_at = SystemTime::now();
}
}
pub fn is_available(&self, transport_type: &str) -> bool {
if let Ok(stats) = self.stats.read() {
if let Some(stats) = stats.get(transport_type) {
return stats.upstream_status == UpstreamStatus::Available ||
stats.upstream_status == UpstreamStatus::Unknown;
}
}
true
}
pub fn get_upstream_status(&self, transport_type: &str) -> UpstreamStatus {
if let Ok(stats) = self.stats.read() {
if let Some(detailed_stats) = stats.get(transport_type) {
return detailed_stats.upstream_status.clone();
}
}
UpstreamStatus::Unknown
}
pub fn get_stats(&self) -> HashMap<String, (u64, u64, Duration)> {
let mut result = HashMap::new();
if let Ok(stats) = self.stats.read() {
for (transport_type, detailed_stats) in stats.iter() {
result.insert(
transport_type.clone(),
(
detailed_stats.success_count,
detailed_stats.failure_count,
Duration::from_millis(detailed_stats.avg_response_time),
),
);
}
}
result
}
pub fn get_detailed_stats(&self) -> HashMap<String, DetailedStats> {
if let Ok(stats) = self.stats.read() {
stats.clone()
} else {
HashMap::new()
}
}
pub fn get_available_transports(&self) -> Vec<String> {
let mut available = Vec::new();
if let Ok(stats) = self.stats.read() {
for (transport_type, detailed_stats) in stats.iter() {
if detailed_stats.upstream_status == UpstreamStatus::Available ||
detailed_stats.upstream_status == UpstreamStatus::Unknown {
available.push(transport_type.clone());
}
}
}
available
}
pub fn is_transport_available(&self, transport_type: &str) -> bool {
if let Ok(stats) = self.stats.read() {
if let Some(detailed_stats) = stats.get(transport_type) {
detailed_stats.upstream_status == UpstreamStatus::Available ||
detailed_stats.upstream_status == UpstreamStatus::Unknown
} else {
true
}
} else {
true
}
}
pub fn get_unavailable_transports(&self) -> Vec<String> {
let mut unavailable = Vec::new();
if let Ok(stats) = self.stats.read() {
for (transport_type, detailed_stats) in stats.iter() {
if detailed_stats.upstream_status == UpstreamStatus::Unavailable {
unavailable.push(transport_type.clone());
}
}
}
unavailable
}
pub fn reset_stats(&self, transport_type: &str) {
if let Ok(mut stats) = self.stats.write() {
if let Some(detailed_stats) = stats.get_mut(transport_type) {
*detailed_stats = DetailedStats::default();
}
}
}
pub fn reset_all_stats(&self) {
if let Ok(mut stats) = self.stats.write() {
stats.clear();
}
}
pub fn set_upstream_status(&self, transport_type: &str, status: UpstreamStatus) {
if let Ok(mut stats) = self.stats.write() {
let detailed_stats = stats.entry(transport_type.to_string())
.or_insert_with(DetailedStats::default);
if detailed_stats.upstream_status != status {
detailed_stats.upstream_status = status;
detailed_stats.status_changed_at = SystemTime::now();
}
}
}
pub fn get_transport_ranking(&self) -> Vec<(String, f64)> {
let mut rankings = Vec::new();
if let Ok(stats) = self.stats.read() {
for (transport_type, detailed_stats) in stats.iter() {
let mut score = 0.0;
match detailed_stats.upstream_status {
UpstreamStatus::Available => score += 100.0,
UpstreamStatus::Unknown => score += 80.0,
UpstreamStatus::Unavailable => score += 0.0,
}
let total = detailed_stats.success_count + detailed_stats.failure_count;
let success_rate = if total > 0 {
detailed_stats.success_count as f64 / total as f64
} else {
0.0
};
score += success_rate * 50.0;
let avg_ms = detailed_stats.avg_response_time as f64;
if avg_ms > 0.0 {
score += (1000.0 / avg_ms.max(1.0)).min(50.0).max(0.0);
}
score += (detailed_stats.consecutive_successes as f64).min(20.0).max(0.0);
score -= (detailed_stats.consecutive_failures as f64) * 5.0;
rankings.push((transport_type.clone(), score.max(0.0)));
}
}
rankings.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
rankings
}
pub fn check_interval(&self) -> Duration {
self.check_interval
}
pub fn set_check_interval(&mut self, interval: Duration) {
self.check_interval = interval;
}
pub fn config(&self) -> &UpstreamConfig {
&self.config
}
pub fn update_config(&mut self, config: UpstreamConfig) {
self.config = config;
}
}
pub struct UpstreamMonitorTask {
monitor: Arc<UpstreamMonitor>,
}
impl UpstreamMonitorTask {
pub fn new(monitor: Arc<UpstreamMonitor>) -> Self {
Self { monitor }
}
pub async fn start(self) {
let mut interval = tokio::time::interval(self.monitor.check_interval());
loop {
interval.tick().await;
self.perform_upstream_monitoring().await;
}
}
async fn perform_upstream_monitoring(&self) {
let stats = self.monitor.get_detailed_stats();
for (transport_type, detailed_stats) in stats {
if let Some(last_success) = detailed_stats.last_success {
if let Ok(elapsed) = last_success.elapsed() {
if elapsed > Duration::from_secs(300) {
}
}
}
if detailed_stats.upstream_status == UpstreamStatus::Unavailable {
if let Ok(elapsed) = detailed_stats.status_changed_at.elapsed() {
if elapsed > self.monitor.config().max_unavailable_duration {
self.monitor.set_upstream_status(&transport_type, UpstreamStatus::Unknown);
}
}
}
}
}
}