1use std::{
2 panic::{AssertUnwindSafe, catch_unwind},
3 time::Duration,
4};
5
6use crate::{PolicyId, ScopeId};
7
8pub trait Observer: Send + Sync + 'static {
18 fn observe(&self, observation: &Observation<'_>);
20}
21
22#[derive(Clone, Copy, Debug, Eq, PartialEq)]
24#[non_exhaustive]
25pub enum Observation<'a> {
26 Admission(AdmissionObservation<'a>),
28 Cleanup(CleanupObservation),
30 Capacity(CapacityObservation),
32}
33
34#[derive(Clone, Copy, Debug, Eq, PartialEq)]
36#[non_exhaustive]
37pub enum AdmissionOperation {
38 Check,
40 Batch,
42}
43
44#[derive(Clone, Copy, Debug, Eq, PartialEq)]
46#[non_exhaustive]
47pub enum AdmissionOutcome {
48 Allowed,
50 QuotaDenied,
52 ShadowDenied,
54 CapacityDenied,
56 Failed,
58}
59
60#[derive(Clone, Copy, Debug, Eq, PartialEq)]
62#[non_exhaustive]
63pub enum ConsumptionStatus {
64 Consumed,
66 NotConsumed,
68 PossiblyConsumed,
70}
71
72#[derive(Clone, Copy, Debug, Eq, PartialEq)]
74pub struct AdmissionObservation<'a> {
75 operation: AdmissionOperation,
76 batch_size: usize,
77 policy_id: Option<&'a PolicyId>,
78 scope_id: Option<&'a ScopeId>,
79 outcome: AdmissionOutcome,
80 consumption: ConsumptionStatus,
81 elapsed: Duration,
82}
83
84impl<'a> AdmissionObservation<'a> {
85 pub const fn new(
87 operation: AdmissionOperation,
88 batch_size: usize,
89 policy_id: Option<&'a PolicyId>,
90 scope_id: Option<&'a ScopeId>,
91 outcome: AdmissionOutcome,
92 consumption: ConsumptionStatus,
93 elapsed: Duration,
94 ) -> Self {
95 Self {
96 operation,
97 batch_size,
98 policy_id,
99 scope_id,
100 outcome,
101 consumption,
102 elapsed,
103 }
104 }
105
106 pub const fn operation(self) -> AdmissionOperation {
108 self.operation
109 }
110
111 pub const fn batch_size(self) -> usize {
113 self.batch_size
114 }
115
116 pub const fn policy_id(self) -> Option<&'a PolicyId> {
118 self.policy_id
119 }
120
121 pub const fn scope_id(self) -> Option<&'a ScopeId> {
123 self.scope_id
124 }
125
126 pub const fn outcome(self) -> AdmissionOutcome {
128 self.outcome
129 }
130
131 pub const fn consumption(self) -> ConsumptionStatus {
133 self.consumption
134 }
135
136 pub const fn elapsed(self) -> Duration {
138 self.elapsed
139 }
140}
141
142#[derive(Clone, Copy, Debug, Eq, PartialEq)]
144pub struct CleanupObservation {
145 requested: usize,
146 removed: Option<u64>,
147 elapsed: Duration,
148 consumption: ConsumptionStatus,
149}
150
151impl CleanupObservation {
152 pub const fn new(
157 requested: usize,
158 removed: Option<u64>,
159 elapsed: Duration,
160 consumption: ConsumptionStatus,
161 ) -> Self {
162 Self {
163 requested,
164 removed,
165 elapsed,
166 consumption,
167 }
168 }
169
170 pub const fn requested(self) -> usize {
172 self.requested
173 }
174
175 pub const fn removed(self) -> Option<u64> {
177 self.removed
178 }
179
180 pub const fn elapsed(self) -> Duration {
182 self.elapsed
183 }
184
185 pub const fn consumption(self) -> ConsumptionStatus {
187 self.consumption
188 }
189}
190
191#[derive(Clone, Copy, Debug, Eq, PartialEq)]
193pub struct CapacityObservation {
194 used: u64,
195 capacity: u64,
196 shard_index: Option<usize>,
197}
198
199impl CapacityObservation {
200 pub const fn new(used: u64, capacity: u64, shard_index: Option<usize>) -> Self {
202 Self {
203 used,
204 capacity,
205 shard_index,
206 }
207 }
208
209 pub const fn used(self) -> u64 {
211 self.used
212 }
213
214 pub const fn capacity(self) -> u64 {
216 self.capacity
217 }
218
219 pub const fn headroom(self) -> u64 {
221 self.capacity.saturating_sub(self.used)
222 }
223
224 pub const fn shard_index(self) -> Option<usize> {
226 self.shard_index
227 }
228}
229
230#[doc(hidden)]
236pub fn observe_safely(observer: &dyn Observer, observation: &Observation<'_>) {
237 let _ = catch_unwind(AssertUnwindSafe(|| observer.observe(observation)));
238}