linkrs_storage/edge/csr_with_properties/
maintenance.rs1use super::{CsrWithProperties, RowVisibility};
2use crate::edge::csr_shared::EdgeIdBitSet;
3use crate::edge::property_schema::PropertySchema;
4use linkrs_core::types::{EdgeId, Timestamp};
5
6impl CsrWithProperties {
7 pub fn reserve_for_batch(&mut self, additional: usize) {
13 if additional == 0 || self.is_inline_stub() {
14 return;
15 }
16 self.visibility.reserve(additional);
17 self.row_to_edge.reserve(additional);
18 self.free_list.reserve(additional);
19 for col in &mut self.property_columns {
20 col.reserve(additional);
21 }
22 }
23
24 pub fn compaction_stats(&self) -> crate::edge::property_schema::PropertyCompactionStats {
25 let tombstone_count = self
26 .visibility
27 .iter()
28 .filter(|v| v.delete_ts.is_some())
29 .count();
30 let live_records = self.visibility.iter().filter(|v| v.is_live()).count();
31 let mut reclaimable_bytes = 0usize;
32 for v in &self.visibility {
33 if v.delete_ts.is_some() {
34 reclaimable_bytes += 32 * self.property_schema.len();
35 }
36 }
37 crate::edge::property_schema::PropertyCompactionStats {
38 tombstone_count,
39 total_records: self.visibility.len(),
40 live_records,
41 reclaimable_bytes,
42 }
43 }
44
45 pub fn gc_property_versions(&mut self, min_active_snapshot_ts: Timestamp) -> usize {
48 self.property_columns
49 .iter_mut()
50 .map(|col| col.gc_versions(min_active_snapshot_ts))
51 .sum()
52 }
53
54 pub fn used_memory_size(&self) -> usize {
55 let mut total = std::mem::size_of::<Self>();
56 total += self.visibility.capacity() * std::mem::size_of::<RowVisibility>();
57 total += self.edge_map_memory_bytes();
58 total += self.free_list.capacity() * std::mem::size_of::<u32>();
59 total += self.row_to_edge.capacity() * std::mem::size_of::<Option<EdgeId>>();
60 total += self.column_index.len()
61 * (std::mem::size_of::<String>() + std::mem::size_of::<usize>());
62 for col in &self.property_columns {
63 total += col.memory_size();
64 }
65 total += self.property_schema.len() * std::mem::size_of::<PropertySchema>();
66 total
67 }
68
69 pub fn mapping_row_bytes(&self) -> (usize, usize) {
73 let mapping = self.edge_map_memory_bytes()
74 + self.row_to_edge.capacity() * std::mem::size_of::<Option<EdgeId>>()
75 + self.free_list.capacity() * std::mem::size_of::<u32>();
76 let mut rows = self.visibility.capacity() * std::mem::size_of::<RowVisibility>();
77 for col in &self.property_columns {
78 rows += col.memory_size();
79 }
80 (mapping, rows)
81 }
82
83 pub(crate) fn reclaim_slots(
84 &mut self,
85 valid_edge_ids: &EdgeIdBitSet,
86 retention_bound: Timestamp,
87 ) -> usize {
88 if retention_bound == Timestamp::MAX {
89 return 0;
90 }
91 let mut to_reclaim = Vec::new();
92 for (idx, vis) in self.visibility.iter().enumerate() {
93 if vis.is_virgin() {
94 continue;
95 }
96 let has_live_edge = self
99 .row_to_edge
100 .get(idx)
101 .and_then(|slot| *slot)
102 .is_some_and(|eid| valid_edge_ids.contains(&eid));
103 if has_live_edge {
104 continue;
105 }
106 if let Some(del_ts) = vis.delete_ts {
107 if crate::mvcc_visibility::Visibility::is_gc_eligible(del_ts, retention_bound) {
110 to_reclaim.push(idx);
111 }
112 }
113 }
114 if to_reclaim.is_empty() {
115 return 0;
116 }
117 for &idx in &to_reclaim {
118 self.visibility[idx].create_ts = 0;
119 self.visibility[idx].delete_ts = None;
120 if let Some(taken) = self.row_to_edge.get_mut(idx).and_then(|slot| slot.take()) {
121 self.map_remove(taken);
122 }
123 self.free_list.push(idx as u32);
126 self.row_count = self.row_count.saturating_sub(1);
127 }
128 to_reclaim.len()
129 }
130
131 pub fn refresh_column_stats(&mut self) {
138 if self.dirty_columns.is_empty() {
139 for col in &mut self.property_columns {
140 if let Ok(stats) = col.compute_stats() {
141 col.set_stats(stats);
142 }
143 }
144 return;
145 }
146 let dirty: Vec<usize> = self.dirty_columns.iter().copied().collect();
149 for idx in dirty {
150 if let Some(col) = self.property_columns.get_mut(idx) {
151 if let Ok(stats) = col.compute_stats() {
152 col.set_stats(stats);
153 }
154 }
155 }
156 }
157
158 pub fn refresh_column_stats_for(&mut self, column: &str) {
161 if let Some(&idx) = self.column_index.get(column) {
162 if let Some(col) = self.property_columns.get_mut(idx) {
163 if let Ok(stats) = col.compute_stats() {
164 col.set_stats(stats);
165 }
166 }
167 }
168 }
169}