1use std::{
5 collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque},
6 mem,
7 ops::Add,
8 sync::Arc,
9};
10
11use reifydb_codec::{
12 key::encoded::EncodedKey,
13 row::{bytes::EncodedBytes, shape::fingerprint::RowShapeFingerprint},
14};
15pub use reifydb_macro::HeapSize;
16use reifydb_value::{
17 byte_size::ByteSize,
18 count::Count,
19 util::hash::Hash128,
20 value::{
21 Value,
22 date::Date,
23 datetime::DateTime,
24 duration::Duration,
25 identity::IdentityId,
26 ordered_f32::OrderedF32,
27 ordered_f64::OrderedF64,
28 partition::Partition,
29 percentile::{Centroid, Percentiles},
30 row_number::RowNumber,
31 time::Time,
32 uuid::{Uuid4, Uuid7},
33 },
34};
35
36use crate::{
37 key::any::TaggedKey,
38 state::{join::ContentVersion, timer::TimerKind},
39 value::index::encoded::EncodedIndexKey,
40};
41
42pub trait HeapSize {
43 fn heap_size(&self) -> usize;
44}
45
46#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
47pub struct StateMemory {
48 pub entries: Count,
49 pub bytes: ByteSize,
50}
51
52impl StateMemory {
53 pub const ZERO: Self = Self {
54 entries: Count::ZERO,
55 bytes: ByteSize::ZERO,
56 };
57
58 pub fn new(entries: Count, bytes: ByteSize) -> Self {
59 Self {
60 entries,
61 bytes,
62 }
63 }
64}
65
66impl Add for StateMemory {
67 type Output = Self;
68
69 fn add(self, rhs: Self) -> Self {
70 Self {
71 entries: self.entries + rhs.entries,
72 bytes: self.bytes + rhs.bytes,
73 }
74 }
75}
76
77#[derive(Clone, Copy, Debug, PartialEq, Eq)]
78pub struct StateCompleteness {
79 pub values_complete: bool,
80 pub membership_complete: bool,
81 pub absences_served: Count,
82 pub false_positives: Count,
83 pub revocations: Count,
84}
85
86impl StateCompleteness {
87 pub const MERGE_IDENTITY: Self = Self {
88 values_complete: true,
89 membership_complete: true,
90 absences_served: Count::ZERO,
91 false_positives: Count::ZERO,
92 revocations: Count::ZERO,
93 };
94
95 pub fn merge(self, rhs: Self) -> Self {
96 Self {
97 values_complete: self.values_complete && rhs.values_complete,
98 membership_complete: self.membership_complete && rhs.membership_complete,
99 absences_served: self.absences_served + rhs.absences_served,
100 false_positives: self.false_positives + rhs.false_positives,
101 revocations: self.revocations + rhs.revocations,
102 }
103 }
104}
105
106#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
107pub struct OperatorSample {
108 pub memory: Option<StateMemory>,
109 pub row_number_cache: Option<StateMemory>,
110}
111
112impl OperatorSample {
113 pub fn with_memory(memory: StateMemory) -> Self {
114 Self {
115 memory: Some(memory),
116 ..Self::default()
117 }
118 }
119
120 pub fn with_row_number_cache(mut self, memory: StateMemory) -> Self {
121 self.row_number_cache = Some(memory);
122 self
123 }
124}
125
126macro_rules! zero_heap {
127 ($($ty:ty),* $(,)?) => {
128 $(impl HeapSize for $ty {
129 fn heap_size(&self) -> usize {
130 0
131 }
132 })*
133 };
134}
135
136zero_heap!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64, bool, char, ());
137zero_heap!(
138 OrderedF32, OrderedF64, Date, DateTime, Time, Duration, IdentityId, Uuid4, Uuid7, RowNumber, Hash128, Centroid
139);
140zero_heap!(Partition, RowShapeFingerprint, TimerKind, ContentVersion);
141
142const BIGNUM_APPROX_HEAP: usize = 32;
143
144impl HeapSize for Percentiles {
145 fn heap_size(&self) -> usize {
146 mem::size_of_val(self.centroids())
147 }
148}
149
150impl HeapSize for String {
151 fn heap_size(&self) -> usize {
152 self.capacity()
153 }
154}
155
156impl HeapSize for EncodedBytes {
157 fn heap_size(&self) -> usize {
158 self.as_slice().len()
159 }
160}
161
162impl HeapSize for EncodedKey {
163 fn heap_size(&self) -> usize {
164 match self {
165 EncodedKey::Inline {
166 ..
167 } => 0,
168 EncodedKey::Shared(bytes) => bytes.len() + 2 * mem::size_of::<usize>(),
169 }
170 }
171}
172
173impl HeapSize for TaggedKey {
174 fn heap_size(&self) -> usize {
175 match self {
176 TaggedKey::SortedViewRow(key) => key.run.len(),
177 TaggedKey::PartitionedSortedViewRow(key) => key.run.len(),
178 TaggedKey::RingBufferMetadata(key) => {
179 key.partition_values.capacity() * mem::size_of::<Value>()
180 + key.partition_values.iter().map(HeapSize::heap_size).sum::<usize>()
181 }
182 TaggedKey::QueueDeduplication(key) => key.tail.heap_size(),
183 TaggedKey::IndexEntry(key) => key.key.heap_size(),
184 TaggedKey::OperatorState(key) => key.suffix.capacity(),
185 _ => 0,
186 }
187 }
188}
189
190impl HeapSize for EncodedIndexKey {
191 fn heap_size(&self) -> usize {
192 match self {
193 EncodedIndexKey::Inline {
194 ..
195 } => 0,
196 EncodedIndexKey::Heap(bytes) => bytes.capacity(),
197 }
198 }
199}
200
201impl<T: HeapSize> HeapSize for Option<T> {
202 fn heap_size(&self) -> usize {
203 self.as_ref().map_or(0, HeapSize::heap_size)
204 }
205}
206
207impl<T: HeapSize> HeapSize for Vec<T> {
208 fn heap_size(&self) -> usize {
209 self.capacity() * mem::size_of::<T>() + self.iter().map(HeapSize::heap_size).sum::<usize>()
210 }
211}
212
213impl<T: HeapSize, const N: usize> HeapSize for [T; N] {
214 fn heap_size(&self) -> usize {
215 self.iter().map(HeapSize::heap_size).sum::<usize>()
216 }
217}
218
219impl<T: HeapSize> HeapSize for VecDeque<T> {
220 fn heap_size(&self) -> usize {
221 self.capacity() * mem::size_of::<T>() + self.iter().map(HeapSize::heap_size).sum::<usize>()
222 }
223}
224
225impl<T: HeapSize> HeapSize for Box<T> {
226 fn heap_size(&self) -> usize {
227 mem::size_of::<T>() + (**self).heap_size()
228 }
229}
230
231impl<T: HeapSize> HeapSize for Arc<T> {
232 fn heap_size(&self) -> usize {
233 mem::size_of::<usize>() * 2 + mem::size_of::<T>() + (**self).heap_size()
234 }
235}
236
237impl HeapSize for Arc<str> {
238 fn heap_size(&self) -> usize {
239 mem::size_of::<usize>() * 2 + self.len()
240 }
241}
242
243impl<T: HeapSize> HeapSize for Arc<[T]> {
244 fn heap_size(&self) -> usize {
245 mem::size_of::<usize>() * 2
246 + self.len() * mem::size_of::<T>()
247 + self.iter().map(HeapSize::heap_size).sum::<usize>()
248 }
249}
250
251impl<T: HeapSize> HeapSize for Box<[T]> {
252 fn heap_size(&self) -> usize {
253 self.len() * mem::size_of::<T>() + self.iter().map(HeapSize::heap_size).sum::<usize>()
254 }
255}
256
257impl HeapSize for Box<str> {
258 fn heap_size(&self) -> usize {
259 self.len()
260 }
261}
262
263impl<K: HeapSize, V: HeapSize> HeapSize for BTreeMap<K, V> {
264 fn heap_size(&self) -> usize {
265 self.len() * (mem::size_of::<K>() + mem::size_of::<V>())
266 + self.iter().map(|(k, v)| k.heap_size() + v.heap_size()).sum::<usize>()
267 }
268}
269
270impl<T: HeapSize> HeapSize for BTreeSet<T> {
271 fn heap_size(&self) -> usize {
272 self.len() * mem::size_of::<T>() + self.iter().map(HeapSize::heap_size).sum::<usize>()
273 }
274}
275
276impl<K: HeapSize, V: HeapSize, S> HeapSize for HashMap<K, V, S> {
277 fn heap_size(&self) -> usize {
278 self.capacity() * (mem::size_of::<K>() + mem::size_of::<V>() + 1)
279 + self.iter().map(|(k, v)| k.heap_size() + v.heap_size()).sum::<usize>()
280 }
281}
282
283impl<T: HeapSize, S> HeapSize for HashSet<T, S> {
284 fn heap_size(&self) -> usize {
285 self.capacity() * (mem::size_of::<T>() + 1) + self.iter().map(HeapSize::heap_size).sum::<usize>()
286 }
287}
288
289impl<A: HeapSize, B: HeapSize> HeapSize for (A, B) {
290 fn heap_size(&self) -> usize {
291 self.0.heap_size() + self.1.heap_size()
292 }
293}
294
295impl<A: HeapSize, B: HeapSize, C: HeapSize> HeapSize for (A, B, C) {
296 fn heap_size(&self) -> usize {
297 self.0.heap_size() + self.1.heap_size() + self.2.heap_size()
298 }
299}
300
301impl HeapSize for Value {
302 fn heap_size(&self) -> usize {
303 match self {
304 Value::Utf8(text) => text.capacity(),
305 Value::Blob(blob) => blob.as_bytes().len(),
306 Value::Int(_) | Value::Uint(_) | Value::Decimal(_) => BIGNUM_APPROX_HEAP,
307 Value::Any(inner) => mem::size_of::<Value>() + inner.heap_size(),
308 Value::List(items) | Value::Tuple(items) => items.heap_size(),
309 Value::Record(fields) => {
310 fields.capacity() * mem::size_of::<(String, Value)>()
311 + fields.iter()
312 .map(|(name, value)| name.capacity() + value.heap_size())
313 .sum::<usize>()
314 }
315 _ => 0,
316 }
317 }
318}
319
320#[cfg(test)]
321mod tests {
322 use super::HeapSize;
323
324 #[derive(HeapSize)]
325 struct DerivedSample {
326 name: String,
327 values: Vec<u64>,
328 count: u64,
329 }
330
331 #[test]
332 fn derived_heap_size_sums_all_fields() {
333 let sample = DerivedSample {
336 name: String::with_capacity(32),
337 values: Vec::with_capacity(4),
338 count: 7,
339 };
340 assert_eq!(sample.count, 7);
341 assert_eq!(sample.heap_size(), 32 + 4 * 8);
342 }
343}