use std::sync::Arc;
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use std::time::{Duration, Instant};
use parking_lot::{Mutex, RwLock};
use tokio::sync::Semaphore;
#[derive(Debug, Clone)]
pub struct BackpressureConfig {
pub max_concurrent: usize,
pub max_queue_depth: usize,
pub target_latency: Duration,
pub shed_threshold: f64,
pub metrics_window: Duration,
pub adaptive_concurrency: bool,
}
impl Default for BackpressureConfig {
fn default() -> Self {
Self {
max_concurrent: 100,
max_queue_depth: 1000,
target_latency: Duration::from_secs(5),
shed_threshold: 0.9,
metrics_window: Duration::from_secs(60),
adaptive_concurrency: true,
}
}
}
impl BackpressureConfig {
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_max_concurrent(mut self, max: usize) -> Self {
self.max_concurrent = max;
self
}
#[must_use]
pub fn with_max_queue_depth(mut self, max: usize) -> Self {
self.max_queue_depth = max;
self
}
#[must_use]
pub fn with_target_latency(mut self, latency: Duration) -> Self {
self.target_latency = latency;
self
}
#[must_use]
pub fn with_adaptive_concurrency(mut self, enabled: bool) -> Self {
self.adaptive_concurrency = enabled;
self
}
#[must_use]
pub fn with_shed_threshold(mut self, threshold: f64) -> Self {
self.shed_threshold = threshold;
self
}
pub fn high_throughput() -> Self {
Self {
max_concurrent: 500,
max_queue_depth: 5000,
target_latency: Duration::from_secs(10),
shed_threshold: 0.95,
metrics_window: Duration::from_secs(30),
adaptive_concurrency: true,
}
}
pub fn low_latency() -> Self {
Self {
max_concurrent: 50,
max_queue_depth: 100,
target_latency: Duration::from_secs(2),
shed_threshold: 0.8,
metrics_window: Duration::from_secs(15),
adaptive_concurrency: true,
}
}
}
#[derive(Debug, Clone)]
struct LatencySample {
latency: Duration,
timestamp: Instant,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AcquireResult {
Acquired,
Queued(usize),
Shed,
}
#[derive(Debug)]
pub struct BackpressurePermit {
controller: Arc<BackpressureController>,
start_time: Instant,
completed: bool,
}
impl BackpressurePermit {
pub fn complete(mut self, success: bool) {
self.completed = true;
let latency = self.start_time.elapsed();
self.controller.record_completion(latency, success);
}
}
impl Drop for BackpressurePermit {
fn drop(&mut self) {
if !self.completed {
self.controller.in_flight.fetch_sub(1, Ordering::Relaxed);
self.controller.semaphore.add_permits(1);
}
}
}
#[derive(Debug)]
pub struct BackpressureController {
config: BackpressureConfig,
semaphore: Arc<Semaphore>,
in_flight: AtomicUsize,
queue_depth: AtomicUsize,
current_limit: AtomicUsize,
total_requests: AtomicU64,
shed_requests: AtomicU64,
samples: Mutex<Vec<LatencySample>>,
last_adaptation: RwLock<Instant>,
}
impl BackpressureController {
pub fn new(config: BackpressureConfig) -> Arc<Self> {
let initial_limit = config.max_concurrent;
Arc::new(Self {
semaphore: Arc::new(Semaphore::new(config.max_concurrent)),
in_flight: AtomicUsize::new(0),
queue_depth: AtomicUsize::new(0),
current_limit: AtomicUsize::new(initial_limit),
total_requests: AtomicU64::new(0),
shed_requests: AtomicU64::new(0),
samples: Mutex::new(Vec::new()),
last_adaptation: RwLock::new(Instant::now()),
config,
})
}
pub async fn acquire(self: &Arc<Self>) -> Result<BackpressurePermit, BackpressureError> {
self.total_requests.fetch_add(1, Ordering::Relaxed);
let load = self.load_factor();
if load >= self.config.shed_threshold {
self.shed_requests.fetch_add(1, Ordering::Relaxed);
return Err(BackpressureError::Overloaded { load_factor: load });
}
let queue = self.queue_depth.fetch_add(1, Ordering::Relaxed);
if queue >= self.config.max_queue_depth {
self.queue_depth.fetch_sub(1, Ordering::Relaxed);
self.shed_requests.fetch_add(1, Ordering::Relaxed);
return Err(BackpressureError::QueueFull {
depth: queue,
max: self.config.max_queue_depth,
});
}
let _permit = self
.semaphore
.acquire()
.await
.map_err(|_| BackpressureError::Closed)?;
self.queue_depth.fetch_sub(1, Ordering::Relaxed);
self.in_flight.fetch_add(1, Ordering::Relaxed);
std::mem::forget(_permit);
Ok(BackpressurePermit {
controller: Arc::clone(self),
start_time: Instant::now(),
completed: false,
})
}
pub fn try_acquire(self: &Arc<Self>) -> Result<BackpressurePermit, BackpressureError> {
self.total_requests.fetch_add(1, Ordering::Relaxed);
let load = self.load_factor();
if load >= self.config.shed_threshold {
self.shed_requests.fetch_add(1, Ordering::Relaxed);
return Err(BackpressureError::Overloaded { load_factor: load });
}
let _permit = self
.semaphore
.try_acquire()
.map_err(|_| BackpressureError::WouldBlock {
in_flight: self.in_flight.load(Ordering::Relaxed),
})?;
self.in_flight.fetch_add(1, Ordering::Relaxed);
std::mem::forget(_permit);
Ok(BackpressurePermit {
controller: Arc::clone(self),
start_time: Instant::now(),
completed: false,
})
}
fn record_completion(&self, latency: Duration, success: bool) {
self.in_flight.fetch_sub(1, Ordering::Relaxed);
self.semaphore.add_permits(1);
if !self.config.adaptive_concurrency {
return;
}
{
let mut samples = self.samples.lock();
samples.push(LatencySample {
latency,
timestamp: Instant::now(),
});
let cutoff = Instant::now() - self.config.metrics_window;
samples.retain(|s| s.timestamp > cutoff);
}
let should_adapt = {
let last = self.last_adaptation.read();
last.elapsed() > Duration::from_secs(1)
};
if should_adapt {
self.adapt_concurrency(success);
}
}
fn adapt_concurrency(&self, success: bool) {
*self.last_adaptation.write() = Instant::now();
let samples = self.samples.lock();
if samples.len() < 10 {
return;
}
let avg_latency: Duration =
samples.iter().map(|s| s.latency).sum::<Duration>() / samples.len() as u32;
let current = self.current_limit.load(Ordering::Relaxed);
let new_limit = if !success || avg_latency > self.config.target_latency {
(current * 3 / 4).max(1)
} else if avg_latency < self.config.target_latency / 2 {
current.saturating_add(1).min(self.config.max_concurrent)
} else {
current
};
if new_limit != current {
self.current_limit.store(new_limit, Ordering::Relaxed);
if new_limit > current {
self.semaphore.add_permits(new_limit - current);
}
}
}
pub fn load_factor(&self) -> f64 {
let in_flight = self.in_flight.load(Ordering::Relaxed);
let limit = self.current_limit.load(Ordering::Relaxed);
if limit == 0 {
return 1.0;
}
in_flight as f64 / limit as f64
}
pub fn stats(&self) -> BackpressureStats {
let samples = self.samples.lock();
let avg_latency = if samples.is_empty() {
Duration::ZERO
} else {
samples.iter().map(|s| s.latency).sum::<Duration>() / samples.len() as u32
};
let p99_latency = if samples.len() >= 100 {
let mut latencies: Vec<_> = samples.iter().map(|s| s.latency).collect();
latencies.sort();
latencies[latencies.len() * 99 / 100]
} else {
avg_latency
};
BackpressureStats {
in_flight: self.in_flight.load(Ordering::Relaxed),
queue_depth: self.queue_depth.load(Ordering::Relaxed),
current_limit: self.current_limit.load(Ordering::Relaxed),
max_limit: self.config.max_concurrent,
total_requests: self.total_requests.load(Ordering::Relaxed),
shed_requests: self.shed_requests.load(Ordering::Relaxed),
load_factor: self.load_factor(),
avg_latency,
p99_latency,
}
}
pub fn reset_stats(&self) {
self.total_requests.store(0, Ordering::Relaxed);
self.shed_requests.store(0, Ordering::Relaxed);
self.samples.lock().clear();
}
}
#[derive(Debug, Clone, thiserror::Error)]
pub enum BackpressureError {
#[error("system overloaded, load factor: {load_factor:.2}")]
Overloaded {
load_factor: f64,
},
#[error("queue full, depth: {depth}, max: {max}")]
QueueFull {
depth: usize,
max: usize,
},
#[error("would block, {in_flight} requests in flight")]
WouldBlock {
in_flight: usize,
},
#[error("backpressure controller closed")]
Closed,
}
#[derive(Debug, Clone)]
pub struct BackpressureStats {
pub in_flight: usize,
pub queue_depth: usize,
pub current_limit: usize,
pub max_limit: usize,
pub total_requests: u64,
pub shed_requests: u64,
pub load_factor: f64,
pub avg_latency: Duration,
pub p99_latency: Duration,
}
impl BackpressureStats {
pub fn shed_rate(&self) -> f64 {
if self.total_requests == 0 {
return 0.0;
}
self.shed_requests as f64 / self.total_requests as f64
}
pub fn is_healthy(&self) -> bool {
self.load_factor < 0.8 && self.shed_rate() < 0.01
}
}
#[derive(Debug)]
pub struct LoadShedder {
threshold: AtomicUsize,
controller: Arc<BackpressureController>,
}
impl LoadShedder {
pub fn new(controller: Arc<BackpressureController>) -> Self {
Self {
threshold: AtomicUsize::new(0),
controller,
}
}
pub fn update(&self) {
let load = self.controller.load_factor();
let threshold = if load > 0.9 {
8 } else if load > 0.8 {
5 } else if load > 0.7 {
3 } else {
0 };
self.threshold.store(threshold, Ordering::Relaxed);
}
pub fn should_accept(&self, priority: u8) -> bool {
let threshold = self.threshold.load(Ordering::Relaxed);
priority as usize >= threshold
}
pub fn threshold(&self) -> usize {
self.threshold.load(Ordering::Relaxed)
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use super::*;
#[tokio::test]
async fn test_backpressure_acquire() {
let config = BackpressureConfig::default().with_max_concurrent(5);
let controller = BackpressureController::new(config);
let permits: Vec<_> = (0..3).map(|_| controller.try_acquire().unwrap()).collect();
assert_eq!(controller.in_flight.load(Ordering::Relaxed), 3);
for permit in permits {
permit.complete(true);
}
assert_eq!(controller.in_flight.load(Ordering::Relaxed), 0);
}
#[tokio::test]
async fn test_backpressure_limit() {
let config = BackpressureConfig::default().with_max_concurrent(2);
let controller = BackpressureController::new(config);
let _p1 = controller.try_acquire().unwrap();
let _p2 = controller.try_acquire().unwrap();
let result = controller.try_acquire();
assert!(result.is_err());
}
#[tokio::test]
async fn test_load_shedding() {
let config = BackpressureConfig::default()
.with_max_concurrent(10)
.with_shed_threshold(0.5);
let controller = BackpressureController::new(config);
let mut permits = Vec::new();
for _ in 0..5 {
permits.push(controller.try_acquire().unwrap());
}
let load = controller.load_factor();
assert!(load >= 0.5);
}
#[test]
fn test_load_shedder() {
let config = BackpressureConfig::default().with_max_concurrent(10);
let controller = BackpressureController::new(config);
let shedder = LoadShedder::new(Arc::clone(&controller));
shedder.update();
assert!(shedder.should_accept(0));
assert!(shedder.should_accept(10));
}
#[test]
fn test_stats() {
let config = BackpressureConfig::default().with_max_concurrent(100);
let controller = BackpressureController::new(config);
let stats = controller.stats();
assert_eq!(stats.in_flight, 0);
assert_eq!(stats.max_limit, 100);
assert!(stats.is_healthy());
}
}