use serde::{Deserialize, Serialize};
use std::collections::VecDeque;
use std::time::Instant;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BandwidthBucket {
pub timestamp_ms: i64,
pub request_bytes: u64,
pub response_bytes: u64,
pub request_count: u64,
}
impl BandwidthBucket {
pub fn new() -> Self {
Self {
timestamp_ms: chrono::Utc::now().timestamp_millis(),
request_bytes: 0,
response_bytes: 0,
request_count: 0,
}
}
pub fn total_bytes(&self) -> u64 {
self.request_bytes + self.response_bytes
}
}
impl Default for BandwidthBucket {
fn default() -> Self {
Self::new()
}
}
pub struct EntityBandwidth {
pub entity_id: String,
pub total_request_bytes: u64,
pub total_response_bytes: u64,
pub total_request_count: u64,
buckets: VecDeque<BandwidthBucket>,
current_bucket: BandwidthBucket,
bucket_duration_ms: u64,
max_buckets: usize,
last_rotation: Instant,
pub first_seen: Instant,
pub last_seen: Instant,
pub access_count: u64,
}
impl EntityBandwidth {
pub fn new(entity_id: String, bucket_duration_ms: u64, max_buckets: usize) -> Self {
let now = Instant::now();
Self {
entity_id,
total_request_bytes: 0,
total_response_bytes: 0,
total_request_count: 0,
buckets: VecDeque::with_capacity(max_buckets),
current_bucket: BandwidthBucket::new(),
bucket_duration_ms,
max_buckets,
last_rotation: now,
first_seen: now,
last_seen: now,
access_count: 0,
}
}
pub fn record(&mut self, request_bytes: u64, response_bytes: u64) {
self.last_seen = Instant::now();
self.access_count += 1;
self.total_request_bytes += request_bytes;
self.total_response_bytes += response_bytes;
self.total_request_count += 1;
if self.last_rotation.elapsed().as_millis() >= self.bucket_duration_ms as u128 {
self.rotate_bucket();
}
self.current_bucket.request_bytes += request_bytes;
self.current_bucket.response_bytes += response_bytes;
self.current_bucket.request_count += 1;
}
fn rotate_bucket(&mut self) {
let old_bucket = std::mem::take(&mut self.current_bucket);
self.buckets.push_back(old_bucket);
self.last_rotation = Instant::now();
while self.buckets.len() > self.max_buckets {
self.buckets.pop_front();
}
}
pub fn avg_bytes_per_minute(&self) -> u64 {
if self.buckets.is_empty() {
return self.current_bucket.total_bytes();
}
let total: u64 = self.buckets.iter().map(|b| b.total_bytes()).sum();
total / self.buckets.len() as u64
}
pub fn current_bytes_per_minute(&self) -> u64 {
self.current_bucket.total_bytes()
}
pub fn total_bytes(&self) -> u64 {
self.total_request_bytes + self.total_response_bytes
}
pub fn recent_buckets(&self) -> Vec<BandwidthBucket> {
self.buckets.iter().cloned().collect()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EntityBandwidthSnapshot {
pub entity_id: String,
pub total_request_bytes: u64,
pub total_response_bytes: u64,
pub total_request_count: u64,
pub bytes_per_minute: u64,
pub first_seen_ms: i64,
pub last_seen_ms: i64,
pub recent_buckets: Vec<BandwidthBucket>,
}
impl From<&EntityBandwidth> for EntityBandwidthSnapshot {
fn from(entity: &EntityBandwidth) -> Self {
let now = chrono::Utc::now().timestamp_millis();
let first_elapsed = entity.first_seen.elapsed().as_millis() as i64;
let last_elapsed = entity.last_seen.elapsed().as_millis() as i64;
Self {
entity_id: entity.entity_id.clone(),
total_request_bytes: entity.total_request_bytes,
total_response_bytes: entity.total_response_bytes,
total_request_count: entity.total_request_count,
bytes_per_minute: entity.avg_bytes_per_minute(),
first_seen_ms: now - first_elapsed,
last_seen_ms: now - last_elapsed,
recent_buckets: entity.recent_buckets(),
}
}
}