noxu_dbi/
memory_budget.rs1use std::sync::atomic::{AtomicI64, Ordering};
2
3pub struct MemoryBudget {
10 max_memory: i64,
12 tree_memory_usage: AtomicI64,
14 lock_memory_usage: AtomicI64,
16 admin_memory_usage: AtomicI64,
18 log_buffer_budget: i64,
20}
21
22pub struct MemoryOverhead;
25
26impl MemoryOverhead {
27 pub const LOCKIMPL_OVERHEAD: i64 = 96;
29 pub const THINLOCKIMPL_OVERHEAD: i64 = 32;
31 pub const LOCKINFO_OVERHEAD: i64 = 32;
33 pub const HASHMAP_ENTRY_OVERHEAD: i64 = 48;
35 pub const LONG_OVERHEAD: i64 = 16;
37 pub const IN_OVERHEAD: i64 = 400;
39 pub const BIN_OVERHEAD: i64 = 500;
41 pub const LN_OVERHEAD: i64 = 48;
43 pub const TXN_OVERHEAD: i64 = 200;
45 pub const BASICLOCKER_OVERHEAD: i64 = 80;
47}
48
49impl MemoryBudget {
50 pub fn new(max_memory: i64) -> Self {
52 let log_buffer_budget = max_memory * 7 / 100;
54
55 MemoryBudget {
56 max_memory,
57 tree_memory_usage: AtomicI64::new(0),
58 lock_memory_usage: AtomicI64::new(0),
59 admin_memory_usage: AtomicI64::new(0),
60 log_buffer_budget,
61 }
62 }
63
64 pub fn max_memory(&self) -> i64 {
66 self.max_memory
67 }
68
69 pub fn log_buffer_budget(&self) -> i64 {
71 self.log_buffer_budget
72 }
73
74 pub fn total_usage(&self) -> i64 {
76 self.tree_memory_usage.load(Ordering::Relaxed)
77 + self.lock_memory_usage.load(Ordering::Relaxed)
78 + self.admin_memory_usage.load(Ordering::Relaxed)
79 }
80
81 pub fn available_memory(&self) -> i64 {
83 self.max_memory - self.total_usage()
84 }
85
86 pub fn is_over_budget(&self) -> bool {
88 self.total_usage() > self.max_memory
89 }
90
91 pub fn get_tree_memory_usage(&self) -> i64 {
93 self.tree_memory_usage.load(Ordering::Relaxed)
94 }
95
96 pub fn update_tree_memory_usage(&self, delta: i64) {
97 self.tree_memory_usage.fetch_add(delta, Ordering::Relaxed);
98 }
99
100 pub fn get_lock_memory_usage(&self) -> i64 {
102 self.lock_memory_usage.load(Ordering::Relaxed)
103 }
104
105 pub fn update_lock_memory_usage(&self, delta: i64) {
106 self.lock_memory_usage.fetch_add(delta, Ordering::Relaxed);
107 }
108
109 pub fn get_admin_memory_usage(&self) -> i64 {
111 self.admin_memory_usage.load(Ordering::Relaxed)
112 }
113
114 pub fn update_admin_memory_usage(&self, delta: i64) {
115 self.admin_memory_usage.fetch_add(delta, Ordering::Relaxed);
116 }
117
118 pub fn reset(&self) {
120 self.tree_memory_usage.store(0, Ordering::Relaxed);
121 self.lock_memory_usage.store(0, Ordering::Relaxed);
122 self.admin_memory_usage.store(0, Ordering::Relaxed);
123 }
124
125 pub fn get_stats(&self) -> MemoryBudgetStats {
127 MemoryBudgetStats {
128 max_memory: self.max_memory,
129 total_usage: self.total_usage(),
130 tree_memory: self.get_tree_memory_usage(),
131 lock_memory: self.get_lock_memory_usage(),
132 admin_memory: self.get_admin_memory_usage(),
133 log_buffer_budget: self.log_buffer_budget,
134 }
135 }
136}
137
138#[derive(Debug, Clone)]
140pub struct MemoryBudgetStats {
141 pub max_memory: i64,
142 pub total_usage: i64,
143 pub tree_memory: i64,
144 pub lock_memory: i64,
145 pub admin_memory: i64,
146 pub log_buffer_budget: i64,
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152
153 #[test]
154 fn test_total_usage_is_sum() {
155 let budget = MemoryBudget::new(1000);
156
157 budget.update_tree_memory_usage(100);
158 budget.update_lock_memory_usage(50);
159 budget.update_admin_memory_usage(25);
160
161 assert_eq!(budget.total_usage(), 175);
162 }
163
164 #[test]
165 fn test_available_memory_calculation() {
166 let budget = MemoryBudget::new(1000);
167
168 assert_eq!(budget.available_memory(), 1000);
169
170 budget.update_tree_memory_usage(300);
171 assert_eq!(budget.available_memory(), 700);
172
173 budget.update_lock_memory_usage(200);
174 assert_eq!(budget.available_memory(), 500);
175 }
176
177 #[test]
178 fn test_is_over_budget() {
179 let budget = MemoryBudget::new(1000);
180
181 assert!(!budget.is_over_budget());
182
183 budget.update_tree_memory_usage(500);
184 assert!(!budget.is_over_budget());
185
186 budget.update_lock_memory_usage(500);
187 assert!(!budget.is_over_budget());
188
189 budget.update_admin_memory_usage(1);
190 assert!(budget.is_over_budget());
191 }
192
193 #[test]
194 fn test_update_with_positive_and_negative_deltas() {
195 let budget = MemoryBudget::new(1000);
196
197 budget.update_tree_memory_usage(500);
198 assert_eq!(budget.get_tree_memory_usage(), 500);
199
200 budget.update_tree_memory_usage(-200);
201 assert_eq!(budget.get_tree_memory_usage(), 300);
202
203 budget.update_tree_memory_usage(-300);
204 assert_eq!(budget.get_tree_memory_usage(), 0);
205 }
206
207 #[test]
208 fn test_reset_clears_all() {
209 let budget = MemoryBudget::new(1000);
210
211 budget.update_tree_memory_usage(100);
212 budget.update_lock_memory_usage(200);
213 budget.update_admin_memory_usage(300);
214
215 budget.reset();
216
217 assert_eq!(budget.get_tree_memory_usage(), 0);
218 assert_eq!(budget.get_lock_memory_usage(), 0);
219 assert_eq!(budget.get_admin_memory_usage(), 0);
220 assert_eq!(budget.total_usage(), 0);
221 }
222
223 #[test]
224 fn test_log_buffer_budget_is_7_percent() {
225 let budget = MemoryBudget::new(10000);
226
227 assert_eq!(budget.log_buffer_budget(), 700);
229 }
230
231 #[test]
232 fn test_memory_overhead_constants_exist() {
233 assert_eq!(MemoryOverhead::LOCKIMPL_OVERHEAD, 96);
235 assert_eq!(MemoryOverhead::THINLOCKIMPL_OVERHEAD, 32);
236 assert_eq!(MemoryOverhead::LOCKINFO_OVERHEAD, 32);
237 assert_eq!(MemoryOverhead::HASHMAP_ENTRY_OVERHEAD, 48);
238 assert_eq!(MemoryOverhead::LONG_OVERHEAD, 16);
239 assert_eq!(MemoryOverhead::IN_OVERHEAD, 400);
240 assert_eq!(MemoryOverhead::BIN_OVERHEAD, 500);
241 assert_eq!(MemoryOverhead::LN_OVERHEAD, 48);
242 assert_eq!(MemoryOverhead::TXN_OVERHEAD, 200);
243 assert_eq!(MemoryOverhead::BASICLOCKER_OVERHEAD, 80);
244 }
245
246 #[test]
247 fn test_get_stats() {
248 let budget = MemoryBudget::new(5000);
249
250 budget.update_tree_memory_usage(1000);
251 budget.update_lock_memory_usage(500);
252 budget.update_admin_memory_usage(250);
253
254 let stats = budget.get_stats();
255
256 assert_eq!(stats.max_memory, 5000);
257 assert_eq!(stats.total_usage, 1750);
258 assert_eq!(stats.tree_memory, 1000);
259 assert_eq!(stats.lock_memory, 500);
260 assert_eq!(stats.admin_memory, 250);
261 assert_eq!(stats.log_buffer_budget, 350); }
263}