oxirs_core/sla/
priority_dispatcher.rs1use std::cmp::Ordering;
12use std::collections::BinaryHeap;
13
14use super::class::SlaClass;
15
16pub struct PrioritizedQuery<T> {
22 pub priority: u8,
24 pub tenant_id: String,
26 pub sequence: u64,
28 pub payload: T,
30}
31
32impl<T> PartialEq for PrioritizedQuery<T> {
36 fn eq(&self, other: &Self) -> bool {
37 self.priority == other.priority && self.sequence == other.sequence
38 }
39}
40
41impl<T> Eq for PrioritizedQuery<T> {}
42
43impl<T> PartialOrd for PrioritizedQuery<T> {
44 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
45 Some(self.cmp(other))
46 }
47}
48
49impl<T> Ord for PrioritizedQuery<T> {
50 fn cmp(&self, other: &Self) -> Ordering {
51 self.priority
53 .cmp(&other.priority)
54 .then_with(|| other.sequence.cmp(&self.sequence))
55 }
56}
57
58pub struct PriorityDispatcher<T> {
80 heap: BinaryHeap<PrioritizedQuery<T>>,
81 next_sequence: u64,
82}
83
84impl<T> Default for PriorityDispatcher<T> {
85 fn default() -> Self {
86 Self::new()
87 }
88}
89
90impl<T> PriorityDispatcher<T> {
91 pub fn new() -> Self {
93 PriorityDispatcher {
94 heap: BinaryHeap::new(),
95 next_sequence: 0,
96 }
97 }
98
99 pub fn enqueue(&mut self, tenant_id: String, sla: SlaClass, payload: T) {
101 let sequence = self.next_sequence;
102 self.next_sequence = self.next_sequence.wrapping_add(1);
103 self.heap.push(PrioritizedQuery {
104 priority: sla.dispatch_priority(),
105 tenant_id,
106 sequence,
107 payload,
108 });
109 }
110
111 pub fn dequeue(&mut self) -> Option<PrioritizedQuery<T>> {
113 self.heap.pop()
114 }
115
116 pub fn peek(&self) -> Option<&PrioritizedQuery<T>> {
118 self.heap.peek()
119 }
120
121 pub fn len(&self) -> usize {
123 self.heap.len()
124 }
125
126 pub fn is_empty(&self) -> bool {
128 self.heap.is_empty()
129 }
130
131 pub fn drain_ordered(&mut self) -> Vec<PrioritizedQuery<T>> {
133 let mut result = Vec::with_capacity(self.heap.len());
134 while let Some(item) = self.heap.pop() {
135 result.push(item);
136 }
137 result
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
146 fn test_platinum_dequeued_first() {
147 let mut d: PriorityDispatcher<&str> = PriorityDispatcher::new();
148 d.enqueue("t_bronze".into(), SlaClass::Bronze, "b");
149 d.enqueue("t_gold".into(), SlaClass::Gold, "g");
150 d.enqueue("t_platinum".into(), SlaClass::Platinum, "p");
151 d.enqueue("t_silver".into(), SlaClass::Silver, "s");
152
153 let first = d.dequeue().expect("non-empty");
154 assert_eq!(first.payload, "p", "Platinum must be first");
155 let second = d.dequeue().expect("non-empty");
156 assert_eq!(second.payload, "g", "Gold must be second");
157 let third = d.dequeue().expect("non-empty");
158 assert_eq!(third.payload, "s", "Silver must be third");
159 let fourth = d.dequeue().expect("non-empty");
160 assert_eq!(fourth.payload, "b", "Bronze must be last");
161 assert!(d.is_empty());
162 }
163
164 #[test]
165 fn test_dequeue_empty_returns_none() {
166 let mut d: PriorityDispatcher<u32> = PriorityDispatcher::new();
167 assert!(d.dequeue().is_none());
168 }
169
170 #[test]
171 fn test_len_and_is_empty() {
172 let mut d: PriorityDispatcher<i32> = PriorityDispatcher::new();
173 assert!(d.is_empty());
174 d.enqueue("t".into(), SlaClass::Silver, 42);
175 assert_eq!(d.len(), 1);
176 assert!(!d.is_empty());
177 d.dequeue();
178 assert!(d.is_empty());
179 }
180
181 #[test]
182 fn test_multiple_same_class_fifo_within_priority() {
183 let mut d: PriorityDispatcher<u32> = PriorityDispatcher::new();
184 for i in 0..5u32 {
185 d.enqueue("gold".into(), SlaClass::Gold, i);
186 }
187 assert_eq!(d.len(), 5);
188 let drained = d.drain_ordered();
190 let payloads: Vec<u32> = drained.iter().map(|q| q.payload).collect();
191 assert_eq!(payloads, vec![0, 1, 2, 3, 4]);
192 }
193
194 #[test]
195 fn test_peek_does_not_remove() {
196 let mut d: PriorityDispatcher<&str> = PriorityDispatcher::new();
197 d.enqueue("t".into(), SlaClass::Platinum, "hello");
198 assert!(d.peek().is_some());
199 assert_eq!(d.len(), 1); d.dequeue();
201 assert!(d.peek().is_none());
202 }
203
204 #[test]
205 fn test_drain_ordered_highest_first() {
206 let mut d: PriorityDispatcher<u8> = PriorityDispatcher::new();
207 d.enqueue("a".into(), SlaClass::Silver, 2);
208 d.enqueue("b".into(), SlaClass::Platinum, 4);
209 d.enqueue("c".into(), SlaClass::Bronze, 1);
210 d.enqueue("d".into(), SlaClass::Gold, 3);
211
212 let drained = d.drain_ordered();
213 let payloads: Vec<u8> = drained.iter().map(|q| q.payload).collect();
214 assert_eq!(payloads, vec![4, 3, 2, 1]);
215 }
216}