use std::sync::atomic::{AtomicUsize, Ordering};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub struct ConnectionCapacityStats {
limit: usize,
in_use: usize,
}
impl ConnectionCapacityStats {
pub(super) fn new(limit: usize, in_use: usize) -> Self {
Self { limit, in_use }
}
pub fn limit(&self) -> usize {
self.limit
}
pub fn in_use(&self) -> usize {
self.in_use
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub struct OriginConnectionStats {
capacity: Option<ConnectionCapacityStats>,
}
impl OriginConnectionStats {
pub(super) fn new(capacity: Option<ConnectionCapacityStats>) -> Self {
Self { capacity }
}
pub fn capacity(&self) -> Option<&ConnectionCapacityStats> {
self.capacity.as_ref()
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[non_exhaustive]
pub struct Http1ConnectionStats {
idle: usize,
active: usize,
draining: usize,
upgraded: usize,
}
impl Http1ConnectionStats {
pub fn idle(&self) -> usize {
self.idle
}
pub fn active(&self) -> usize {
self.active
}
pub fn draining(&self) -> usize {
self.draining
}
pub fn upgraded(&self) -> usize {
self.upgraded
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[non_exhaustive]
pub struct Http2ConnectionStats {
accepting: usize,
draining: usize,
active_requests: usize,
}
impl Http2ConnectionStats {
pub fn accepting(&self) -> usize {
self.accepting
}
pub fn draining(&self) -> usize {
self.draining
}
pub fn active_requests(&self) -> usize {
self.active_requests
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[non_exhaustive]
pub struct PartitionConnectionStats {
pending_acquisitions: usize,
establishing_connections: usize,
h1: Http1ConnectionStats,
h2: Http2ConnectionStats,
physically_live_connections: usize,
}
impl PartitionConnectionStats {
pub fn pending_acquisitions(&self) -> usize {
self.pending_acquisitions
}
pub fn establishing_connections(&self) -> usize {
self.establishing_connections
}
pub fn h1(&self) -> &Http1ConnectionStats {
&self.h1
}
pub fn h2(&self) -> &Http2ConnectionStats {
&self.h2
}
pub fn physically_live_connections(&self) -> usize {
self.physically_live_connections
}
}
#[derive(Debug, Default)]
pub(super) struct CellConnectionStats {
establishing: AtomicUsize,
h1_draining: AtomicUsize,
h1_upgraded: AtomicUsize,
h2_draining: AtomicUsize,
physically_live: AtomicUsize,
}
impl CellConnectionStats {
pub(super) fn establishment_started(&self) {
increment(
&self.establishing,
"connection establishment count exhausted",
);
}
pub(super) fn establishment_finished(&self) {
decrement(
&self.establishing,
"connection establishment completed without a matching start",
);
}
pub(super) fn physical_connection_started(&self) {
increment(&self.physically_live, "physical connection count exhausted");
}
pub(super) fn physical_connection_finished(&self) {
decrement(
&self.physically_live,
"physical connection completed without a matching start",
);
}
pub(super) fn h1_drain_started(&self) {
increment(&self.h1_draining, "HTTP/1 draining count exhausted");
}
pub(super) fn h1_drain_finished(&self) {
decrement(
&self.h1_draining,
"HTTP/1 drain completed without a matching start",
);
}
pub(super) fn h1_drain_upgraded(&self) {
self.h1_drain_finished();
increment(&self.h1_upgraded, "HTTP/1 upgraded count exhausted");
}
pub(super) fn h1_upgrade_started(&self) {
increment(&self.h1_upgraded, "HTTP/1 upgraded count exhausted");
}
pub(super) fn h1_upgrade_finished(&self) {
decrement(
&self.h1_upgraded,
"HTTP/1 upgrade completed without a matching start",
);
}
pub(super) fn h2_drain_started(&self) {
increment(&self.h2_draining, "HTTP/2 draining count exhausted");
}
pub(super) fn h2_drain_finished(&self) {
decrement(
&self.h2_draining,
"HTTP/2 drain completed without a matching start",
);
}
pub(super) fn snapshot(
&self,
pending_acquisitions: usize,
h1_idle: usize,
h1_active: usize,
h2_accepting: usize,
h2_active_requests: usize,
) -> PartitionConnectionStats {
PartitionConnectionStats {
pending_acquisitions,
establishing_connections: self.establishing.load(Ordering::Relaxed),
h1: Http1ConnectionStats {
idle: h1_idle,
active: h1_active,
draining: self.h1_draining.load(Ordering::Relaxed),
upgraded: self.h1_upgraded.load(Ordering::Relaxed),
},
h2: Http2ConnectionStats {
accepting: h2_accepting,
draining: self.h2_draining.load(Ordering::Relaxed),
active_requests: h2_active_requests,
},
physically_live_connections: self.physically_live.load(Ordering::Relaxed),
}
}
}
fn increment(counter: &AtomicUsize, message: &'static str) {
counter
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |value| {
value.checked_add(1)
})
.expect(message);
}
fn decrement(counter: &AtomicUsize, message: &'static str) {
counter
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |value| {
value.checked_sub(1)
})
.expect(message);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn snapshot_combines_exact_and_lifetime_state() {
let stats = CellConnectionStats::default();
stats.establishment_started();
stats.physical_connection_started();
stats.h1_drain_started();
stats.h1_drain_upgraded();
stats.h2_drain_started();
let snapshot = stats.snapshot(2, 3, 4, 5, 6);
assert_eq!(2, snapshot.pending_acquisitions());
assert_eq!(1, snapshot.establishing_connections());
assert_eq!(3, snapshot.h1().idle());
assert_eq!(4, snapshot.h1().active());
assert_eq!(0, snapshot.h1().draining());
assert_eq!(1, snapshot.h1().upgraded());
assert_eq!(5, snapshot.h2().accepting());
assert_eq!(1, snapshot.h2().draining());
assert_eq!(6, snapshot.h2().active_requests());
assert_eq!(1, snapshot.physically_live_connections());
}
}