redisson 0.1.0

A Redis-based distributed synchronization and data structures library for Rust
Documentation
/*
 *
 *  *
 *  *      Copyright (c) 2018-2025, SnackCloud All rights reserved.
 *  *
 *  *   Redistribution and use in source and binary forms, with or without
 *  *   modification, are permitted provided that the following conditions are met:
 *  *
 *  *   Redistributions of source code must retain the above copyright notice,
 *  *   this list of conditions and the following disclaimer.
 *  *   Redistributions in binary form must reproduce the above copyright
 *  *   notice, this list of conditions and the following disclaimer in the
 *  *   documentation and/or other materials provided with the distribution.
 *  *   Neither the name of the www.snackcloud.cn developer nor the names of its
 *  *   contributors may be used to endorse or promote products derived from
 *  *   this software without specific prior written permission.
 *  *   Author: SnackCloud
 *  *
 *  
 */

use crate::network_latency::LatencyStats;
use parking_lot::Mutex;
use std::collections::VecDeque;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Duration;

/// Network latency statistics
pub struct NetworkLatencyStats {
    samples: Mutex<VecDeque<Duration>>,
    max_samples: usize,
    current_estimate: AtomicU64, // It's stored in nanoseconds
}

impl NetworkLatencyStats {
    pub fn new(max_samples: usize) -> Self {
        Self {
            samples: Mutex::new(VecDeque::with_capacity(max_samples)),
            max_samples,
            current_estimate: AtomicU64::new(10_000_000), // 默认10ms
        }
    }

    /// Add a new delayed sample
    pub fn add_sample(&self, latency: Duration) {
        let mut samples = self.samples.lock();

        // Adding new samples
        samples.push_back(latency);

        // Keep the number of samples not exceeding the maximum value
        if samples.len() > self.max_samples {
            samples.pop_front();
        }

        // Recalculate the estimate (using P95)
        if samples.len() >= 3 {
            let mut sorted: Vec<Duration> = samples.iter().copied().collect();
            sorted.sort();

            // Calculate P95 (95th percentile)
            let index = (sorted.len() as f64 * 0.95).floor() as usize;
            let p95_latency = sorted[index.min(sorted.len() - 1)];

            // Plus a little margin of safety (times 1.5)
            let estimate = p95_latency * 3 / 2;
            self.current_estimate.store(estimate.as_nanos() as u64, Ordering::Release);
        }
    }

    /// Get the current delay estimate
    pub fn get_estimate(&self) -> Duration {
        Duration::from_nanos(self.current_estimate.load(Ordering::Acquire))
    }

    /// Get latency statistics
    pub fn get_stats(&self) -> LatencyStats {
        let samples = self.samples.lock();
        let mut sorted: Vec<Duration> = samples.iter().copied().collect();
        sorted.sort();

        let count = sorted.len();
        if count == 0 {
            return LatencyStats {
                min: Duration::from_millis(0),
                max: Duration::from_millis(0),
                avg: Duration::from_millis(0),
                p95: Duration::from_millis(0),
                p99: Duration::from_millis(0),
                count: 0,
            };
        }

        let min = sorted[0];
        let max = sorted[count - 1];
        let sum: Duration = sorted.iter().sum();
        let avg = sum / count as u32;

        let p95_index = (count as f64 * 0.95).floor() as usize;
        let p95 = sorted[p95_index.min(count - 1)];

        let p99_index = (count as f64 * 0.99).floor() as usize;
        let p99 = sorted[p99_index.min(count - 1)];

        LatencyStats {
            min,
            max,
            avg,
            p95,
            p99,
            count,
        }
    }
}