use std::{
panic::{AssertUnwindSafe, catch_unwind},
time::Duration,
};
use crate::{PolicyId, ScopeId};
pub trait Observer: Send + Sync + 'static {
fn observe(&self, observation: &Observation<'_>);
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum Observation<'a> {
Admission(AdmissionObservation<'a>),
Cleanup(CleanupObservation),
Capacity(CapacityObservation),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum AdmissionOperation {
Check,
Batch,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum AdmissionOutcome {
Allowed,
QuotaDenied,
ShadowDenied,
CapacityDenied,
Failed,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ConsumptionStatus {
Consumed,
NotConsumed,
PossiblyConsumed,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct AdmissionObservation<'a> {
operation: AdmissionOperation,
batch_size: usize,
policy_id: Option<&'a PolicyId>,
scope_id: Option<&'a ScopeId>,
outcome: AdmissionOutcome,
consumption: ConsumptionStatus,
elapsed: Duration,
}
impl<'a> AdmissionObservation<'a> {
pub const fn new(
operation: AdmissionOperation,
batch_size: usize,
policy_id: Option<&'a PolicyId>,
scope_id: Option<&'a ScopeId>,
outcome: AdmissionOutcome,
consumption: ConsumptionStatus,
elapsed: Duration,
) -> Self {
Self {
operation,
batch_size,
policy_id,
scope_id,
outcome,
consumption,
elapsed,
}
}
pub const fn operation(self) -> AdmissionOperation {
self.operation
}
pub const fn batch_size(self) -> usize {
self.batch_size
}
pub const fn policy_id(self) -> Option<&'a PolicyId> {
self.policy_id
}
pub const fn scope_id(self) -> Option<&'a ScopeId> {
self.scope_id
}
pub const fn outcome(self) -> AdmissionOutcome {
self.outcome
}
pub const fn consumption(self) -> ConsumptionStatus {
self.consumption
}
pub const fn elapsed(self) -> Duration {
self.elapsed
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct CleanupObservation {
requested: usize,
removed: Option<u64>,
elapsed: Duration,
consumption: ConsumptionStatus,
}
impl CleanupObservation {
pub const fn new(
requested: usize,
removed: Option<u64>,
elapsed: Duration,
consumption: ConsumptionStatus,
) -> Self {
Self {
requested,
removed,
elapsed,
consumption,
}
}
pub const fn requested(self) -> usize {
self.requested
}
pub const fn removed(self) -> Option<u64> {
self.removed
}
pub const fn elapsed(self) -> Duration {
self.elapsed
}
pub const fn consumption(self) -> ConsumptionStatus {
self.consumption
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct CapacityObservation {
used: u64,
capacity: u64,
shard_index: Option<usize>,
}
impl CapacityObservation {
pub const fn new(used: u64, capacity: u64, shard_index: Option<usize>) -> Self {
Self {
used,
capacity,
shard_index,
}
}
pub const fn used(self) -> u64 {
self.used
}
pub const fn capacity(self) -> u64 {
self.capacity
}
pub const fn headroom(self) -> u64 {
self.capacity.saturating_sub(self.used)
}
pub const fn shard_index(self) -> Option<usize> {
self.shard_index
}
}
#[doc(hidden)]
pub fn observe_safely(observer: &dyn Observer, observation: &Observation<'_>) {
let _ = catch_unwind(AssertUnwindSafe(|| observer.observe(observation)));
}