1use std::collections::BTreeMap;
13
14use crate::error::{CoreError, CoreResult};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub enum LedgerType {
19 Root,
21 Domain,
23 Queue,
25 Connection,
27}
28
29impl LedgerType {
30 pub fn depth(&self) -> u8 {
32 match self {
33 LedgerType::Root => 0,
34 LedgerType::Domain => 1,
35 LedgerType::Queue => 2,
36 LedgerType::Connection => 3,
37 }
38 }
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
43pub enum ResourceType {
44 Memory,
46 Frames,
48 Connections,
50}
51
52impl ResourceType {
53 pub fn name(&self) -> &'static str {
55 match self {
56 ResourceType::Memory => "memory",
57 ResourceType::Frames => "frames",
58 ResourceType::Connections => "connections",
59 }
60 }
61}
62
63#[derive(Debug, Clone, Default)]
65pub struct LedgerQuota {
66 pub memory_bytes: u64,
68 pub frame_count: u64,
70 pub connection_count: u64,
72}
73
74impl LedgerQuota {
75 #[inline]
77 pub const fn zero() -> Self {
78 Self {
79 memory_bytes: 0,
80 frame_count: 0,
81 connection_count: 0,
82 }
83 }
84
85 #[inline]
87 pub const fn unlimited() -> Self {
88 Self {
89 memory_bytes: u64::MAX,
90 frame_count: u64::MAX,
91 connection_count: u64::MAX,
92 }
93 }
94
95 #[inline]
97 pub fn can_allocate(&self, child: &LedgerQuota) -> CoreResult<()> {
98 if child.memory_bytes > self.memory_bytes {
99 return Err(CoreError::quota_exceeded(
100 "memory",
101 self.memory_bytes,
102 child.memory_bytes,
103 ));
104 }
105 if child.frame_count > self.frame_count {
106 return Err(CoreError::quota_exceeded(
107 "frames",
108 self.frame_count,
109 child.frame_count,
110 ));
111 }
112 if child.connection_count > self.connection_count {
113 return Err(CoreError::quota_exceeded(
114 "connections",
115 self.connection_count,
116 child.connection_count,
117 ));
118 }
119 Ok(())
120 }
121
122 #[inline]
124 pub fn consume(&mut self, resource: ResourceType, amount: u64) -> CoreResult<()> {
125 let field = match resource {
126 ResourceType::Memory => &mut self.memory_bytes,
127 ResourceType::Frames => &mut self.frame_count,
128 ResourceType::Connections => &mut self.connection_count,
129 };
130 *field = field
131 .checked_sub(amount)
132 .ok_or_else(|| CoreError::arithmetic_overflow("sub", *field, amount))?;
133 Ok(())
134 }
135
136 #[inline]
138 pub fn restore(&mut self, resource: ResourceType, amount: u64) -> CoreResult<()> {
139 let field = match resource {
140 ResourceType::Memory => &mut self.memory_bytes,
141 ResourceType::Frames => &mut self.frame_count,
142 ResourceType::Connections => &mut self.connection_count,
143 };
144 *field = field
145 .checked_add(amount)
146 .ok_or_else(|| CoreError::arithmetic_overflow("add", *field, amount))?;
147 Ok(())
148 }
149
150 #[inline]
152 pub fn get(&self, resource: ResourceType) -> u64 {
153 match resource {
154 ResourceType::Memory => self.memory_bytes,
155 ResourceType::Frames => self.frame_count,
156 ResourceType::Connections => self.connection_count,
157 }
158 }
159
160 pub fn remaining(&self, used: &LedgerQuota) -> LedgerQuota {
162 LedgerQuota {
163 memory_bytes: self.memory_bytes.saturating_sub(used.memory_bytes),
164 frame_count: self.frame_count.saturating_sub(used.frame_count),
165 connection_count: self.connection_count.saturating_sub(used.connection_count),
166 }
167 }
168}
169
170#[derive(Debug)]
178pub struct ResourceLedger {
179 name: String,
181 ledger_type: LedgerType,
183 total: LedgerQuota,
185 used: LedgerQuota,
187 used_direct: LedgerQuota,
193 allocated_to_children: LedgerQuota,
195 children: BTreeMap<String, ResourceLedger>,
197}
198
199impl ResourceLedger {
200 pub fn new(name: impl Into<String>, ledger_type: LedgerType, total: LedgerQuota) -> Self {
202 Self {
203 name: name.into(),
204 ledger_type,
205 total,
206 used: LedgerQuota::zero(),
207 used_direct: LedgerQuota::zero(),
208 allocated_to_children: LedgerQuota::zero(),
209 children: BTreeMap::new(),
210 }
211 }
212
213 pub fn root(total: LedgerQuota) -> Self {
215 Self::new("root", LedgerType::Root, total)
216 }
217
218 #[inline]
220 pub fn name(&self) -> &str {
221 &self.name
222 }
223
224 #[inline]
226 pub fn ledger_type(&self) -> LedgerType {
227 self.ledger_type
228 }
229
230 #[inline]
232 pub fn depth(&self) -> u8 {
233 self.ledger_type.depth()
234 }
235
236 #[inline]
238 pub fn total(&self) -> &LedgerQuota {
239 &self.total
240 }
241
242 #[inline]
244 pub fn used(&self) -> &LedgerQuota {
245 &self.used
246 }
247
248 #[inline]
250 pub fn allocated_to_children(&self) -> &LedgerQuota {
251 &self.allocated_to_children
252 }
253
254 #[inline]
256 pub fn child_count(&self) -> usize {
257 self.children.len()
258 }
259
260 pub fn child_names(&self) -> impl Iterator<Item = &str> {
262 self.children.keys().map(|s| s.as_str())
263 }
264
265 pub fn get_child(&self, name: &str) -> Option<&ResourceLedger> {
267 self.children.get(name)
268 }
269
270 pub fn get_child_mut(&mut self, name: &str) -> Option<&mut ResourceLedger> {
272 self.children.get_mut(name)
273 }
274
275 pub fn create_child(
288 &mut self,
289 name: impl Into<String>,
290 ledger_type: LedgerType,
291 quota: LedgerQuota,
292 ) -> CoreResult<&mut ResourceLedger> {
293 let name: String = name.into();
294
295 if quota.memory_bytes == 0
297 && quota.frame_count == 0
298 && quota.connection_count == 0
299 {
300 return Err(CoreError::invalid_config(
301 "LedgerQuota",
302 "all quota fields are zero; a child ledger with zero total quota can never allocate any resource",
303 ));
304 }
305
306 if self.children.contains_key(&name) {
307 return Err(CoreError::resource_already_exists(
308 0,
309 "ledger",
310 ));
311 }
312
313 for resource in [ResourceType::Memory, ResourceType::Frames, ResourceType::Connections] {
316 let committed = self
317 .allocated_to_children
318 .get(resource)
319 .checked_add(quota.get(resource))
320 .ok_or_else(|| {
321 CoreError::arithmetic_overflow(
322 "add",
323 self.allocated_to_children.get(resource),
324 quota.get(resource),
325 )
326 })?;
327 let available = self
329 .total
330 .get(resource)
331 .checked_sub(self.used.get(resource))
332 .ok_or_else(|| {
333 CoreError::arithmetic_overflow(
334 "sub",
335 self.total.get(resource),
336 self.used.get(resource),
337 )
338 })?;
339 if committed > available {
340 return Err(CoreError::quota_exceeded(
341 resource.name(),
342 available,
343 committed,
344 ));
345 }
346 }
347
348 self.allocated_to_children.memory_bytes = self
351 .allocated_to_children
352 .memory_bytes
353 .checked_add(quota.memory_bytes)
354 .ok_or_else(|| {
355 CoreError::arithmetic_overflow(
356 "add",
357 self.allocated_to_children.memory_bytes,
358 quota.memory_bytes,
359 )
360 })?;
361 self.allocated_to_children.frame_count = self
362 .allocated_to_children
363 .frame_count
364 .checked_add(quota.frame_count)
365 .ok_or_else(|| {
366 CoreError::arithmetic_overflow(
367 "add",
368 self.allocated_to_children.frame_count,
369 quota.frame_count,
370 )
371 })?;
372 self.allocated_to_children.connection_count = self
373 .allocated_to_children
374 .connection_count
375 .checked_add(quota.connection_count)
376 .ok_or_else(|| {
377 CoreError::arithmetic_overflow(
378 "add",
379 self.allocated_to_children.connection_count,
380 quota.connection_count,
381 )
382 })?;
383
384 let child = ResourceLedger::new(name.clone(), ledger_type, quota);
385 self.children.insert(name.clone(), child);
386
387 self.children.get_mut(&name).ok_or_else(|| CoreError::internal("child ledger not found after insertion"))
388 }
389
390 pub fn remove_child(&mut self, name: &str) -> CoreResult<()> {
398 let child = self
399 .children
400 .remove(name)
401 .ok_or_else(|| CoreError::resource_not_found(0, "ledger"))?;
402
403 self.used.memory_bytes = self
407 .used
408 .memory_bytes
409 .checked_sub(child.used.memory_bytes)
410 .ok_or_else(|| {
411 CoreError::arithmetic_overflow(
412 "sub",
413 self.used.memory_bytes,
414 child.used.memory_bytes,
415 )
416 })?;
417 self.used.frame_count = self
418 .used
419 .frame_count
420 .checked_sub(child.used.frame_count)
421 .ok_or_else(|| {
422 CoreError::arithmetic_overflow(
423 "sub",
424 self.used.frame_count,
425 child.used.frame_count,
426 )
427 })?;
428 self.used.connection_count = self
429 .used
430 .connection_count
431 .checked_sub(child.used.connection_count)
432 .ok_or_else(|| {
433 CoreError::arithmetic_overflow(
434 "sub",
435 self.used.connection_count,
436 child.used.connection_count,
437 )
438 })?;
439
440 self.allocated_to_children.memory_bytes = self
442 .allocated_to_children
443 .memory_bytes
444 .checked_sub(child.total.memory_bytes)
445 .ok_or_else(|| {
446 CoreError::arithmetic_overflow(
447 "sub",
448 self.allocated_to_children.memory_bytes,
449 child.total.memory_bytes,
450 )
451 })?;
452 self.allocated_to_children.frame_count = self
453 .allocated_to_children
454 .frame_count
455 .checked_sub(child.total.frame_count)
456 .ok_or_else(|| {
457 CoreError::arithmetic_overflow(
458 "sub",
459 self.allocated_to_children.frame_count,
460 child.total.frame_count,
461 )
462 })?;
463 self.allocated_to_children.connection_count = self
464 .allocated_to_children
465 .connection_count
466 .checked_sub(child.total.connection_count)
467 .ok_or_else(|| {
468 CoreError::arithmetic_overflow(
469 "sub",
470 self.allocated_to_children.connection_count,
471 child.total.connection_count,
472 )
473 })?;
474
475 Ok(())
476 }
477
478 pub fn allocate(&mut self, resource: ResourceType, amount: u64) -> CoreResult<()> {
491 let direct = self.used_direct.get(resource);
492 let total = self.total.get(resource);
493 let new_direct = direct
494 .checked_add(amount)
495 .ok_or_else(|| CoreError::arithmetic_overflow("add", direct, amount))?;
496
497 let committed = self.allocated_to_children.get(resource);
499 let total_need = new_direct
500 .checked_add(committed)
501 .ok_or_else(|| CoreError::arithmetic_overflow("add", new_direct, committed))?;
502 if total_need > total {
503 return Err(CoreError::quota_exceeded(resource.name(), total, total_need));
504 }
505
506 self.used_direct.restore(resource, amount)?;
508 self.used.restore(resource, amount)?;
509 Ok(())
510 }
511
512 fn allocate_rollup(&mut self, resource: ResourceType, amount: u64) -> CoreResult<()> {
518 let current = self.used.get(resource);
519 let total = self.total.get(resource);
520 let new_used = current
521 .checked_add(amount)
522 .ok_or_else(|| CoreError::arithmetic_overflow("add", current, amount))?;
523 if new_used > total {
524 return Err(CoreError::quota_exceeded(resource.name(), total, new_used));
525 }
526 self.used.restore(resource, amount)?;
527 Ok(())
528 }
529
530 pub fn release(&mut self, resource: ResourceType, amount: u64) -> CoreResult<()> {
535 self.used_direct.consume(resource, amount)?;
536 self.used.consume(resource, amount)?;
537 Ok(())
538 }
539
540 fn release_rollup(&mut self, resource: ResourceType, amount: u64) -> CoreResult<()> {
545 self.used.consume(resource, amount)?;
546 Ok(())
547 }
548
549 pub fn allocate_in_child(
567 &mut self,
568 child_path: &[&str],
569 resource: ResourceType,
570 amount: u64,
571 ) -> CoreResult<()> {
572 match child_path.split_first() {
573 None => self.allocate(resource, amount),
574 Some((head, rest)) => {
575 if !self.children.contains_key(*head) {
577 return Err(CoreError::resource_not_found(0, "ledger"));
578 }
579 self.allocate_rollup(resource, amount)?;
583 let child = self
584 .children
585 .get_mut(*head)
586 .ok_or_else(|| CoreError::internal("child ledger not found after existence check"))?;
587 match child.allocate_in_child(rest, resource, amount) {
588 Ok(()) => Ok(()),
589 Err(e) => {
590 self.release_rollup(resource, amount)?;
595 Err(e)
596 }
597 }
598 }
599 }
600 }
601
602 pub fn release_in_child(
615 &mut self,
616 child_path: &[&str],
617 resource: ResourceType,
618 amount: u64,
619 ) -> CoreResult<()> {
620 match child_path.split_first() {
621 None => self.release(resource, amount),
622 Some((head, rest)) => {
623 if !self.children.contains_key(*head) {
625 return Err(CoreError::resource_not_found(0, "ledger"));
626 }
627 self.release_rollup(resource, amount)?;
630 let child = self
631 .children
632 .get_mut(*head)
633 .ok_or_else(|| CoreError::internal("child ledger not found after existence check"))?;
634 match child.release_in_child(rest, resource, amount) {
635 Ok(()) => Ok(()),
636 Err(e) => {
637 self.used.restore(resource, amount)?;
640 Err(e)
641 }
642 }
643 }
644 }
645 }
646
647 #[inline]
652 pub fn can_allocate(&self, resource: ResourceType, amount: u64) -> bool {
653 let direct = self.used_direct.get(resource);
654 let total = self.total.get(resource);
655 let committed = self.allocated_to_children.get(resource);
656 match direct
657 .checked_add(amount)
658 .and_then(|v| v.checked_add(committed))
659 {
660 Some(need) => need <= total,
661 None => false,
663 }
664 }
665
666 pub fn remaining(&self) -> LedgerQuota {
668 self.total.remaining(&self.used)
669 }
670
671 pub fn max_depth(&self) -> u8 {
673 let mut max = self.depth();
674 for child in self.children.values() {
675 max = max.max(child.max_depth());
676 }
677 max
678 }
679
680 pub fn node_count(&self) -> u64 {
682 self.children
686 .values()
687 .map(ResourceLedger::node_count)
688 .fold(1u64, u64::saturating_add)
689 }
690}
691
692#[cfg(test)]
693mod tests {
694 use super::*;
695
696 fn make_quota(mem: u64, frames: u64, conns: u64) -> LedgerQuota {
697 LedgerQuota {
698 memory_bytes: mem,
699 frame_count: frames,
700 connection_count: conns,
701 }
702 }
703
704 #[test]
705 fn test_root_ledger_create() {
706 let root = ResourceLedger::root(make_quota(1 << 30, 100_000, 10_000));
707 assert_eq!(root.name(), "root");
708 assert_eq!(root.ledger_type(), LedgerType::Root);
709 assert_eq!(root.depth(), 0);
710 assert_eq!(root.total().memory_bytes, 1 << 30);
711 assert_eq!(root.used().memory_bytes, 0);
712 assert_eq!(root.child_count(), 0);
713 assert_eq!(root.node_count(), 1);
714 }
715
716 #[test]
717 fn test_create_domain_child() {
718 let mut root = ResourceLedger::root(make_quota(1 << 30, 100_000, 10_000));
719
720 root.create_child("domain_0", LedgerType::Domain, make_quota(1 << 20, 10_000, 1_000))
721 .unwrap();
722
723 assert_eq!(root.child_count(), 1);
724 assert!(root.get_child("domain_0").is_some());
725
726 let domain = root.get_child("domain_0").unwrap();
727 assert_eq!(domain.ledger_type(), LedgerType::Domain);
728 assert_eq!(domain.depth(), 1);
729 assert_eq!(domain.total().frame_count, 10_000);
730 assert_eq!(root.node_count(), 2);
731 }
732
733 #[test]
734 fn test_create_child_quota_exceeded() {
735 let mut root = ResourceLedger::root(make_quota(1024, 100, 10));
736
737 let result = root.create_child("big", LedgerType::Domain, make_quota(2048, 0, 0));
739 assert!(result.is_err());
740
741 assert_eq!(root.child_count(), 0);
743 assert_eq!(root.total().memory_bytes, 1024);
744 }
745
746 #[test]
747 fn test_duplicate_child_name() {
748 let mut root = ResourceLedger::root(make_quota(1 << 30, 100_000, 10_000));
749
750 root.create_child("domain_0", LedgerType::Domain, make_quota(1 << 20, 10_000, 1_000))
751 .unwrap();
752
753 let result = root.create_child("domain_0", LedgerType::Domain, make_quota(1 << 20, 10_000, 1_000));
754 assert!(result.is_err());
755 }
756
757 #[test]
758 fn test_allocate_and_release() {
759 let mut ledger = ResourceLedger::new("test", LedgerType::Queue, make_quota(4096, 100, 10));
760
761 ledger.allocate(ResourceType::Memory, 1024).unwrap();
763 assert_eq!(ledger.used().memory_bytes, 1024);
764
765 ledger.release(ResourceType::Memory, 512).unwrap();
767 assert_eq!(ledger.used().memory_bytes, 512);
768
769 let remaining = ledger.remaining();
771 assert_eq!(remaining.memory_bytes, 4096 - 512);
772 }
773
774 #[test]
775 fn test_allocate_quota_exceeded() {
776 let mut ledger = ResourceLedger::new("small", LedgerType::Queue, make_quota(1024, 0, 0));
777
778 ledger.allocate(ResourceType::Memory, 512).unwrap();
779
780 let result = ledger.allocate(ResourceType::Memory, 1024);
781 assert!(result.is_err());
782 }
783
784 #[test]
785 fn test_allocate_overflow() {
786 let mut ledger = ResourceLedger::new("max", LedgerType::Queue, make_quota(u64::MAX, 0, 0));
787
788 ledger.allocate(ResourceType::Memory, u64::MAX).unwrap();
789
790 let result = ledger.allocate(ResourceType::Memory, 1);
791 assert!(result.is_err());
792 }
793
794 #[test]
795 fn test_frames_and_connections() {
796 let mut ledger = ResourceLedger::new("test", LedgerType::Queue, make_quota(0, 1000, 50));
797
798 ledger.allocate(ResourceType::Frames, 500).unwrap();
799 assert_eq!(ledger.used().frame_count, 500);
800
801 ledger.allocate(ResourceType::Connections, 10).unwrap();
802 assert_eq!(ledger.used().connection_count, 10);
803
804 ledger.release(ResourceType::Frames, 200).unwrap();
805 assert_eq!(ledger.used().frame_count, 300);
806 }
807
808 #[test]
809 fn test_can_allocate() {
810 let ledger = ResourceLedger::new("test", LedgerType::Queue, make_quota(1 << 30, 0, 0));
811
812 assert!(ledger.can_allocate(ResourceType::Memory, 1024));
813 assert!(ledger.can_allocate(ResourceType::Memory, 1 << 30));
814 assert!(!ledger.can_allocate(ResourceType::Memory, (1 << 30) + 1));
815 }
816
817 #[test]
818 fn test_nested_hierarchy() {
819 let mut root = ResourceLedger::root(make_quota(1 << 30, 100_000, 10_000));
820
821 root.create_child("domain_0", LedgerType::Domain, make_quota(1 << 24, 50_000, 5_000))
823 .unwrap();
824
825 root.get_child_mut("domain_0").unwrap()
827 .create_child("queue_0", LedgerType::Queue, make_quota(1 << 20, 10_000, 1_000))
828 .unwrap();
829
830 root.get_child_mut("domain_0").unwrap()
832 .get_child_mut("queue_0").unwrap()
833 .create_child("conn_0", LedgerType::Connection, make_quota(1 << 16, 100, 10))
834 .unwrap();
835
836 let conn = root.get_child("domain_0").unwrap()
838 .get_child("queue_0").unwrap()
839 .get_child("conn_0").unwrap();
840 assert_eq!(conn.depth(), 3);
841 assert_eq!(conn.ledger_type(), LedgerType::Connection);
842 assert_eq!(root.max_depth(), 3);
843 assert_eq!(root.node_count(), 4);
844
845 root.get_child_mut("domain_0").unwrap()
847 .get_child_mut("queue_0").unwrap()
848 .get_child_mut("conn_0").unwrap()
849 .allocate(ResourceType::Memory, 1024)
850 .unwrap();
851
852 assert_eq!(
853 root.get_child("domain_0").unwrap()
854 .get_child("queue_0").unwrap()
855 .get_child("conn_0").unwrap()
856 .used()
857 .memory_bytes,
858 1024
859 );
860 }
861
862 #[test]
863 fn test_remove_child_reclaims_quota() {
864 let mut root = ResourceLedger::root(make_quota(1 << 30, 100_000, 10_000));
865
866 root.create_child("domain_0", LedgerType::Domain, make_quota(1 << 20, 10_000, 1_000))
867 .unwrap();
868
869 root.get_child_mut("domain_0").unwrap()
871 .create_child("queue_0", LedgerType::Queue, make_quota(1 << 16, 1000, 100))
872 .unwrap();
873
874 root.get_child_mut("domain_0").unwrap()
875 .get_child_mut("queue_0").unwrap()
876 .allocate(ResourceType::Memory, 4096)
877 .unwrap();
878
879 let queue_used = root.get_child("domain_0").unwrap()
881 .get_child("queue_0").unwrap()
882 .used().memory_bytes;
883 assert_eq!(queue_used, 4096);
884
885 root.remove_child("domain_0").unwrap();
888
889 assert_eq!(root.child_count(), 0);
890 assert_eq!(root.allocated_to_children().memory_bytes, 0);
892 }
893
894 #[test]
895 fn test_remove_nonexistent_child() {
896 let mut root = ResourceLedger::root(make_quota(1 << 30, 0, 0));
897 let result = root.remove_child("nonexistent");
898 assert!(result.is_err());
899 }
900
901 #[test]
902 fn test_list_child_names() {
903 let mut root = ResourceLedger::root(make_quota(1 << 30, 100_000, 10_000));
904
905 root.create_child("domain_0", LedgerType::Domain, make_quota(1 << 20, 10_000, 1_000))
906 .unwrap();
907 root.create_child("domain_1", LedgerType::Domain, make_quota(1 << 20, 10_000, 1_000))
908 .unwrap();
909
910 let names: Vec<&str> = root.child_names().collect();
911 assert_eq!(names.len(), 2);
912 assert!(names.contains(&"domain_0"));
913 assert!(names.contains(&"domain_1"));
914 }
915
916 #[test]
917 fn test_allocations_accounted_in_parent() {
918 let mut root = ResourceLedger::root(make_quota(1 << 30, 100_000, 10_000));
919
920 root.create_child("domain_0", LedgerType::Domain, make_quota(1 << 20, 10_000, 1_000))
921 .unwrap();
922
923 root.get_child_mut("domain_0").unwrap()
925 .allocate(ResourceType::Memory, 8192)
926 .unwrap();
927
928 assert_eq!(root.get_child("domain_0").unwrap().used().memory_bytes, 8192);
930
931 let domain = root.get_child("domain_0").unwrap();
933 assert!(domain.can_allocate(ResourceType::Memory, (1 << 20) - 8192));
934 assert!(!domain.can_allocate(ResourceType::Memory, (1 << 20) - 8191));
935 }
936
937 #[test]
940 fn test_ledger_quota_zero() {
941 let q = LedgerQuota::zero();
942 assert_eq!(q.memory_bytes, 0);
943 assert_eq!(q.frame_count, 0);
944 assert_eq!(q.connection_count, 0);
945 }
946
947 #[test]
948 fn test_ledger_quota_unlimited() {
949 let q = LedgerQuota::unlimited();
950 assert_eq!(q.memory_bytes, u64::MAX);
951 assert_eq!(q.frame_count, u64::MAX);
952 assert_eq!(q.connection_count, u64::MAX);
953 }
954
955 #[test]
956 fn test_ledger_quota_can_allocate_memory() {
957 let parent = make_quota(1024, 0, 0);
958 let child_ok = make_quota(512, 0, 0);
959 let child_exceed = make_quota(2048, 0, 0);
960
961 assert!(parent.can_allocate(&child_ok).is_ok());
962 assert!(parent.can_allocate(&child_exceed).is_err());
963 }
964
965 #[test]
966 fn test_ledger_quota_can_allocate_frames() {
967 let parent = make_quota(0, 100, 0);
968 let child_ok = make_quota(0, 50, 0);
969 let child_exceed = make_quota(0, 200, 0);
970
971 assert!(parent.can_allocate(&child_ok).is_ok());
972 assert!(parent.can_allocate(&child_exceed).is_err());
973 }
974
975 #[test]
976 fn test_ledger_quota_can_allocate_connections() {
977 let parent = make_quota(0, 0, 10);
978 let child_ok = make_quota(0, 0, 5);
979 let child_exceed = make_quota(0, 0, 20);
980
981 assert!(parent.can_allocate(&child_ok).is_ok());
982 assert!(parent.can_allocate(&child_exceed).is_err());
983 }
984
985 #[test]
986 fn test_ledger_quota_can_allocate_exact() {
987 let parent = make_quota(100, 200, 300);
988 let child_exact = make_quota(100, 200, 300);
989 assert!(parent.can_allocate(&child_exact).is_ok());
990 }
991
992 #[test]
993 fn test_ledger_quota_consume_and_restore() {
994 let mut q = make_quota(1000, 100, 10);
995
996 q.consume(ResourceType::Memory, 500).unwrap();
997 assert_eq!(q.memory_bytes, 500);
998
999 q.restore(ResourceType::Memory, 300).unwrap();
1000 assert_eq!(q.memory_bytes, 800);
1001 }
1002
1003 #[test]
1004 fn test_ledger_quota_consume_underflow() {
1005 let mut q = make_quota(100, 0, 0);
1006 let result = q.consume(ResourceType::Memory, 200);
1007 assert!(result.is_err());
1008 }
1009
1010 #[test]
1011 fn test_ledger_quota_restore_overflow() {
1012 let mut q = make_quota(u64::MAX, 0, 0);
1013 let result = q.restore(ResourceType::Memory, 1);
1014 assert!(result.is_err());
1015 }
1016
1017 #[test]
1018 fn test_ledger_quota_get() {
1019 let q = make_quota(10, 20, 30);
1020 assert_eq!(q.get(ResourceType::Memory), 10);
1021 assert_eq!(q.get(ResourceType::Frames), 20);
1022 assert_eq!(q.get(ResourceType::Connections), 30);
1023 }
1024
1025 #[test]
1026 fn test_ledger_quota_remaining() {
1027 let total = make_quota(1000, 100, 50);
1028 let used = make_quota(400, 30, 10);
1029 let remaining = total.remaining(&used);
1030 assert_eq!(remaining.memory_bytes, 600);
1031 assert_eq!(remaining.frame_count, 70);
1032 assert_eq!(remaining.connection_count, 40);
1033 }
1034
1035 #[test]
1036 fn test_ledger_quota_remaining_saturating() {
1037 let total = make_quota(100, 0, 0);
1038 let used = make_quota(200, 0, 0);
1039 let remaining = total.remaining(&used);
1040 assert_eq!(remaining.memory_bytes, 0);
1041 }
1042
1043 #[test]
1046 fn test_ledger_type_depth() {
1047 assert_eq!(LedgerType::Root.depth(), 0);
1048 assert_eq!(LedgerType::Domain.depth(), 1);
1049 assert_eq!(LedgerType::Queue.depth(), 2);
1050 assert_eq!(LedgerType::Connection.depth(), 3);
1051 }
1052
1053 #[test]
1054 fn test_ledger_type_equality() {
1055 assert_eq!(LedgerType::Root, LedgerType::Root);
1056 assert_ne!(LedgerType::Root, LedgerType::Domain);
1057 }
1058
1059 #[test]
1060 fn test_ledger_type_debug() {
1061 assert_eq!(format!("{:?}", LedgerType::Root), "Root");
1062 assert_eq!(format!("{:?}", LedgerType::Connection), "Connection");
1063 }
1064
1065 #[test]
1068 fn test_resource_type_name() {
1069 assert_eq!(ResourceType::Memory.name(), "memory");
1070 assert_eq!(ResourceType::Frames.name(), "frames");
1071 assert_eq!(ResourceType::Connections.name(), "connections");
1072 }
1073
1074 #[test]
1075 fn test_resource_type_equality() {
1076 assert_eq!(ResourceType::Memory, ResourceType::Memory);
1077 assert_ne!(ResourceType::Memory, ResourceType::Frames);
1078 }
1079
1080 #[test]
1081 fn test_resource_type_debug() {
1082 assert_eq!(format!("{:?}", ResourceType::Memory), "Memory");
1083 assert_eq!(format!("{:?}", ResourceType::Frames), "Frames");
1084 }
1085
1086 #[test]
1089 fn test_multiple_children_quota_sum() {
1090 let mut root = ResourceLedger::root(make_quota(10000, 1000, 100));
1091
1092 root.create_child("d0", LedgerType::Domain, make_quota(3000, 300, 30)).unwrap();
1093 root.create_child("d1", LedgerType::Domain, make_quota(2000, 200, 20)).unwrap();
1094 root.create_child("d2", LedgerType::Domain, make_quota(5000, 500, 50)).unwrap();
1095
1096 let allocated = root.allocated_to_children();
1097 assert_eq!(allocated.memory_bytes, 10000);
1098 assert_eq!(allocated.frame_count, 1000);
1099 assert_eq!(allocated.connection_count, 100);
1100 }
1101
1102 #[test]
1103 fn test_nested_three_level_quota_conservation() {
1104 let mut root = ResourceLedger::root(make_quota(10000, 1000, 100));
1105
1106 root.create_child("d0", LedgerType::Domain, make_quota(5000, 500, 50)).unwrap();
1107
1108 root.get_child_mut("d0").unwrap()
1109 .create_child("q0", LedgerType::Queue, make_quota(2000, 200, 20)).unwrap();
1110 root.get_child_mut("d0").unwrap()
1111 .create_child("q1", LedgerType::Queue, make_quota(3000, 300, 30)).unwrap();
1112
1113 let domain_allocated = root.get_child("d0").unwrap().allocated_to_children();
1114 assert_eq!(domain_allocated.memory_bytes, 5000);
1115 assert_eq!(domain_allocated.frame_count, 500);
1116 assert_eq!(domain_allocated.connection_count, 50);
1117
1118 let root_allocated = root.allocated_to_children();
1119 assert_eq!(root_allocated.memory_bytes, 5000);
1120 }
1121
1122 #[test]
1123 fn test_child_quota_exceeds_parent_total() {
1124 let mut root = ResourceLedger::root(make_quota(1000, 100, 10));
1125
1126 let result = root.create_child("big", LedgerType::Domain, make_quota(2000, 200, 20));
1127 assert!(result.is_err());
1128
1129 assert_eq!(root.child_count(), 0);
1130 assert_eq!(root.allocated_to_children().memory_bytes, 0);
1131 }
1132
1133 #[test]
1134 fn test_child_quota_exceeds_parent_used_remaining() {
1135 let mut root = ResourceLedger::root(make_quota(1000, 100, 10));
1136
1137 root.allocate(ResourceType::Memory, 600).unwrap();
1138 root.allocate(ResourceType::Frames, 60).unwrap();
1139 root.allocate(ResourceType::Connections, 6).unwrap();
1140
1141 let result = root.create_child("d0", LedgerType::Domain, make_quota(500, 50, 5));
1142 assert!(result.is_err());
1143
1144 assert_eq!(root.child_count(), 0);
1145 }
1146
1147 #[test]
1150 fn test_release_underflow_memory() {
1151 let mut ledger = ResourceLedger::new("test", LedgerType::Queue, make_quota(1000, 0, 0));
1152 ledger.allocate(ResourceType::Memory, 500).unwrap();
1153 ledger.release(ResourceType::Memory, 300).unwrap();
1154 assert_eq!(ledger.used().memory_bytes, 200);
1155 }
1156
1157 #[test]
1158 fn test_release_underflow_frames() {
1159 let mut ledger = ResourceLedger::new("test", LedgerType::Queue, make_quota(0, 100, 0));
1160 ledger.allocate(ResourceType::Frames, 50).unwrap();
1161 let result = ledger.release(ResourceType::Frames, 100);
1162 assert!(result.is_err());
1163 }
1164
1165 #[test]
1166 fn test_release_underflow_connections() {
1167 let mut ledger = ResourceLedger::new("test", LedgerType::Queue, make_quota(0, 0, 10));
1168 let result = ledger.release(ResourceType::Connections, 1);
1169 assert!(result.is_err());
1170 }
1171
1172 #[test]
1175 fn test_allocated_to_children_after_remove() {
1176 let mut root = ResourceLedger::root(make_quota(10000, 1000, 100));
1177
1178 root.create_child("d0", LedgerType::Domain, make_quota(3000, 300, 30)).unwrap();
1179 root.create_child("d1", LedgerType::Domain, make_quota(2000, 200, 20)).unwrap();
1180
1181 assert_eq!(root.allocated_to_children().memory_bytes, 5000);
1182 assert_eq!(root.allocated_to_children().frame_count, 500);
1183 assert_eq!(root.allocated_to_children().connection_count, 50);
1184
1185 root.remove_child("d0").unwrap();
1186
1187 assert_eq!(root.allocated_to_children().memory_bytes, 2000);
1188 assert_eq!(root.allocated_to_children().frame_count, 200);
1189 assert_eq!(root.allocated_to_children().connection_count, 20);
1190 }
1191
1192 #[test]
1193 fn test_allocated_to_children_zero_initially() {
1194 let root = ResourceLedger::root(make_quota(1000, 100, 10));
1195 assert_eq!(root.allocated_to_children().memory_bytes, 0);
1196 assert_eq!(root.allocated_to_children().frame_count, 0);
1197 assert_eq!(root.allocated_to_children().connection_count, 0);
1198 }
1199
1200 #[test]
1203 fn test_ledger_quota_default() {
1204 let q = LedgerQuota::default();
1205 assert_eq!(q.memory_bytes, 0);
1206 assert_eq!(q.frame_count, 0);
1207 assert_eq!(q.connection_count, 0);
1208 }
1209
1210 #[test]
1213 fn test_ledger_remaining() {
1214 let mut ledger = ResourceLedger::new("test", LedgerType::Queue, make_quota(1000, 100, 10));
1215 ledger.allocate(ResourceType::Memory, 400).unwrap();
1216 ledger.allocate(ResourceType::Frames, 30).unwrap();
1217 ledger.allocate(ResourceType::Connections, 5).unwrap();
1218
1219 let remaining = ledger.remaining();
1220 assert_eq!(remaining.memory_bytes, 600);
1221 assert_eq!(remaining.frame_count, 70);
1222 assert_eq!(remaining.connection_count, 5);
1223 }
1224
1225 #[test]
1228 fn test_four_level_hierarchy_allocated_to_children() {
1229 let mut root = ResourceLedger::root(make_quota(1 << 30, 100_000, 10_000));
1230
1231 root.create_child("d0", LedgerType::Domain, make_quota(1 << 20, 10_000, 1_000)).unwrap();
1232
1233 root.get_child_mut("d0").unwrap()
1234 .create_child("q0", LedgerType::Queue, make_quota(1 << 15, 1_000, 100)).unwrap();
1235
1236 root.get_child_mut("d0").unwrap().get_child_mut("q0").unwrap()
1237 .create_child("c0", LedgerType::Connection, make_quota(1 << 10, 100, 10)).unwrap();
1238
1239 assert_eq!(root.max_depth(), 3);
1240 assert_eq!(root.node_count(), 4);
1241
1242 let conn = root.get_child("d0").unwrap().get_child("q0").unwrap().get_child("c0").unwrap();
1243 assert_eq!(conn.ledger_type(), LedgerType::Connection);
1244 assert_eq!(conn.depth(), 3);
1245 }
1246
1247 #[test]
1250 fn test_create_child_oversubscription_rejected() {
1251 let mut root = ResourceLedger::root(make_quota(1000, 100, 10));
1252
1253 root.create_child("c1", LedgerType::Domain, make_quota(600, 60, 6)).unwrap();
1255 let result = root.create_child("c2", LedgerType::Domain, make_quota(600, 0, 0));
1257 assert!(result.is_err());
1258 assert_eq!(root.child_count(), 1);
1259
1260 root.create_child("c2", LedgerType::Domain, make_quota(400, 40, 4)).unwrap();
1262 assert_eq!(root.child_count(), 2);
1263 assert_eq!(root.allocated_to_children().memory_bytes, 1000);
1264
1265 assert!(root.create_child("c3", LedgerType::Domain, make_quota(1, 0, 0)).is_err());
1267 }
1268
1269 #[test]
1270 fn test_create_child_accounts_parent_used() {
1271 let mut root = ResourceLedger::root(make_quota(1000, 0, 0));
1272
1273 root.allocate(ResourceType::Memory, 400).unwrap();
1275 root.create_child("c1", LedgerType::Domain, make_quota(600, 0, 0)).unwrap();
1276 assert!(root.create_child("c2", LedgerType::Domain, make_quota(1, 0, 0)).is_err());
1277 }
1278
1279 #[test]
1282 fn test_allocate_in_child_rolls_up_to_ancestors() {
1283 let mut root = ResourceLedger::root(make_quota(1 << 30, 100_000, 10_000));
1284 root.create_child("d0", LedgerType::Domain, make_quota(1 << 20, 10_000, 1_000)).unwrap();
1285 root.get_child_mut("d0").unwrap()
1286 .create_child("q0", LedgerType::Queue, make_quota(1 << 15, 1_000, 100)).unwrap();
1287
1288 root.allocate_in_child(&["d0", "q0"], ResourceType::Memory, 1024).unwrap();
1290
1291 assert_eq!(root.used().memory_bytes, 1024);
1292 assert_eq!(root.get_child("d0").unwrap().used().memory_bytes, 1024);
1293 assert_eq!(
1294 root.get_child("d0").unwrap().get_child("q0").unwrap().used().memory_bytes,
1295 1024
1296 );
1297
1298 root.release_in_child(&["d0", "q0"], ResourceType::Memory, 1024).unwrap();
1300 assert_eq!(root.used().memory_bytes, 0);
1301 assert_eq!(root.get_child("d0").unwrap().used().memory_bytes, 0);
1302 assert_eq!(
1303 root.get_child("d0").unwrap().get_child("q0").unwrap().used().memory_bytes,
1304 0
1305 );
1306 }
1307
1308 #[test]
1309 fn test_allocate_in_child_ancestor_quota_enforced() {
1310 let mut root = ResourceLedger::root(make_quota(1000, 0, 0));
1311 root.create_child("d0", LedgerType::Domain, make_quota(500, 0, 0)).unwrap();
1312
1313 assert!(root.allocate(ResourceType::Memory, 600).is_err());
1316 root.allocate(ResourceType::Memory, 500).unwrap();
1318
1319 root.allocate_in_child(&["d0"], ResourceType::Memory, 500).unwrap();
1321 assert_eq!(root.used().memory_bytes, 1000);
1322 assert_eq!(root.get_child("d0").unwrap().used().memory_bytes, 500);
1323
1324 let result = root.allocate_in_child(&["d0"], ResourceType::Memory, 100);
1326 assert!(result.is_err());
1327 assert_eq!(root.used().memory_bytes, 1000);
1328 assert_eq!(root.get_child("d0").unwrap().used().memory_bytes, 500);
1329 }
1330
1331 #[test]
1332 fn test_allocate_in_child_rollback_on_descendant_failure() {
1333 let mut root = ResourceLedger::root(make_quota(1000, 0, 0));
1334 root.create_child("d0", LedgerType::Domain, make_quota(500, 0, 0)).unwrap();
1335
1336 let result = root.allocate_in_child(&["d0"], ResourceType::Memory, 600);
1338 assert!(result.is_err());
1339 assert_eq!(root.used().memory_bytes, 0);
1340 assert_eq!(root.get_child("d0").unwrap().used().memory_bytes, 0);
1341 }
1342
1343 #[test]
1346 fn test_allocate_not_double_counted_with_child_usage() {
1347 let mut root = ResourceLedger::root(make_quota(1000, 0, 0));
1348 root.create_child("d0", LedgerType::Domain, make_quota(500, 0, 0)).unwrap();
1349
1350 root.allocate_in_child(&["d0"], ResourceType::Memory, 200).unwrap();
1352 assert_eq!(root.used().memory_bytes, 200);
1353 assert_eq!(root.get_child("d0").unwrap().used().memory_bytes, 200);
1354
1355 root.allocate(ResourceType::Memory, 400).unwrap();
1359
1360 assert_eq!(root.used().memory_bytes, 600);
1361 assert_eq!(root.get_child("d0").unwrap().used().memory_bytes, 200);
1362 assert_eq!(root.remaining().memory_bytes, 400);
1364 }
1365
1366 #[test]
1367 fn test_allocate_in_child_rollback_does_not_touch_used_direct() {
1368 let mut root = ResourceLedger::root(make_quota(1000, 0, 0));
1369 root.create_child("d0", LedgerType::Domain, make_quota(500, 0, 0)).unwrap();
1370
1371 root.allocate(ResourceType::Memory, 300).unwrap();
1374 let result = root.allocate_in_child(&["d0"], ResourceType::Memory, 600);
1375 assert!(result.is_err());
1376 assert_eq!(root.used().memory_bytes, 300);
1378 }
1379
1380 #[test]
1381 fn test_allocate_in_child_unknown_path() {
1382 let mut root = ResourceLedger::root(make_quota(1000, 0, 0));
1383 root.create_child("d0", LedgerType::Domain, make_quota(500, 0, 0)).unwrap();
1384
1385 assert!(root.allocate_in_child(&["nope"], ResourceType::Memory, 100).is_err());
1387 assert!(root.allocate_in_child(&["d0", "nope"], ResourceType::Memory, 100).is_err());
1388 assert_eq!(root.used().memory_bytes, 0);
1389 assert_eq!(root.get_child("d0").unwrap().used().memory_bytes, 0);
1390 }
1391
1392 #[test]
1393 fn test_release_in_child_restores_on_descendant_failure() {
1394 let mut root = ResourceLedger::root(make_quota(1000, 0, 0));
1395 root.create_child("d0", LedgerType::Domain, make_quota(500, 0, 0)).unwrap();
1396
1397 root.allocate_in_child(&["d0"], ResourceType::Memory, 200).unwrap();
1398
1399 let result = root.release_in_child(&["d0"], ResourceType::Memory, 300);
1401 assert!(result.is_err());
1402 assert_eq!(root.used().memory_bytes, 200);
1403 assert_eq!(root.get_child("d0").unwrap().used().memory_bytes, 200);
1404
1405 root.release_in_child(&["d0"], ResourceType::Memory, 200).unwrap();
1407 assert_eq!(root.used().memory_bytes, 0);
1408 }
1409
1410 #[test]
1411 fn test_remove_child_reclaims_rolled_up_usage() {
1412 let mut root = ResourceLedger::root(make_quota(1000, 100, 10));
1413 root.create_child("d0", LedgerType::Domain, make_quota(500, 50, 5)).unwrap();
1414
1415 root.allocate_in_child(&["d0"], ResourceType::Memory, 200).unwrap();
1417 assert_eq!(root.used().memory_bytes, 200);
1418
1419 root.remove_child("d0").unwrap();
1421 assert_eq!(root.used().memory_bytes, 0);
1422 assert_eq!(root.allocated_to_children().memory_bytes, 0);
1423
1424 root.create_child("d1", LedgerType::Domain, make_quota(1000, 100, 10)).unwrap();
1426 }
1427}