linkrs_storage/edge/single_mutable_csr/
maintenance.rs1use linkrs_core::StorageResult;
5
6use super::super::csr_shared::{
7 can_revert_delete, decide_slot_delete, is_reclaimable_cold, DeleteSlotOutcome,
8};
9use super::super::{EdgeId, EdgePosition, Timestamp, INVALID_EDGE_ID};
10use super::{empty_slot, SingleMutableCsr};
11
12impl SingleMutableCsr {
13 pub fn delete_edge_at_position(
18 &mut self,
19 src: u32,
20 position: EdgePosition,
21 expected: EdgeId,
22 ts: Timestamp,
23 ) -> StorageResult<bool> {
24 if !matches!(position, EdgePosition::Primary { slot: 0 }) {
25 return Ok(false);
26 }
27 let src_idx = src as usize;
28 if src_idx >= self.vertex_capacity() {
29 return Ok(false);
30 }
31 let probe = match self.slot_at(src_idx) {
32 Some(probe) => probe,
33 None => return Ok(false),
34 };
35 if probe.edge_id == INVALID_EDGE_ID || probe.edge_id != expected {
36 return Ok(false);
37 }
38 if matches!(
39 decide_slot_delete(&probe, expected, ts)?,
40 DeleteSlotOutcome::AlreadyStamped
41 ) {
42 return Ok(false);
43 }
44 self.stamp_delete(src_idx, ts);
45 self.edge_count -= 1;
46 Ok(true)
47 }
48
49 pub fn revert_delete_at_position(
51 &mut self,
52 src: u32,
53 position: EdgePosition,
54 expected: EdgeId,
55 ts: Timestamp,
56 ) -> bool {
57 if !matches!(position, EdgePosition::Primary { slot: 0 }) {
58 return false;
59 }
60 let src_idx = src as usize;
61 if src_idx >= self.vertex_capacity() {
62 return false;
63 }
64 let probe = match self.slot_at(src_idx) {
65 Some(probe) => probe,
66 None => return false,
67 };
68 if probe.edge_id == INVALID_EDGE_ID || probe.edge_id != expected {
69 return false;
70 }
71 if can_revert_delete(&probe, ts) {
72 self.clear_delete(src_idx);
73 self.edge_count += 1;
74 return true;
75 }
76 false
77 }
78
79 pub fn delete_edge_by_dst_reporting_positioned(
81 &mut self,
82 src: u32,
83 dst: super::super::VertexId,
84 ts: Timestamp,
85 on_deleted: &mut dyn FnMut(EdgeId, Option<EdgePosition>),
86 ) -> usize {
87 let src_idx = src as usize;
88 if src_idx >= self.vertex_capacity() {
89 return 0;
90 }
91 let (dst_ep, dst_rank) = match super::super::csr_shared::decode_endpoint_pair(dst) {
92 Some(pair) => pair,
93 None => return 0,
94 };
95 let probe = match self.slot_at(src_idx) {
96 Some(probe) => probe,
97 None => return 0,
98 };
99 if probe.edge_id == INVALID_EDGE_ID
100 || probe.endpoint != dst_ep
101 || probe.rank != dst_rank
102 || !self.cold_is_live_at(src_idx)
103 {
104 return 0;
105 }
106 self.stamp_delete(src_idx, ts);
107 self.edge_count -= 1;
108 on_deleted(probe.edge_id, Some(EdgePosition::Primary { slot: 0 }));
109 1
110 }
111
112 pub fn reclaimable_count(&self, vid: u32, cutoff: Timestamp) -> usize {
113 if cutoff == Timestamp::MAX {
114 return 0;
115 }
116 let hot = self
117 .hot_at(vid as usize)
118 .unwrap_or(super::super::HotNbr::dead_gap());
119 let cold = self
120 .cold_at(vid as usize)
121 .unwrap_or(super::super::ColdStamps::dead_gap());
122 if hot.edge_id != INVALID_EDGE_ID && is_reclaimable_cold(&cold, cutoff) {
123 1
124 } else {
125 0
126 }
127 }
128
129 pub fn vertex_census(&self, vid: u32) -> (usize, usize, usize) {
130 let hot = match self.hot_at(vid as usize) {
131 Some(h) => h,
132 None => return (0, 0, 0),
133 };
134 let cold = self
135 .cold_at(vid as usize)
136 .unwrap_or(super::super::ColdStamps::dead_gap());
137 if hot.edge_id == INVALID_EDGE_ID {
138 return (0, 0, 0);
139 }
140 if cold.is_live() {
141 (1, 0, 1)
142 } else {
143 (0, 1, 1)
144 }
145 }
146
147 pub fn vertex_reclaim_probe(&self, vid: u32, cutoff: Timestamp) -> (usize, usize) {
149 let hot = match self.hot_at(vid as usize) {
150 Some(h) => h,
151 None => return (0, 0),
152 };
153 let cold = self
154 .cold_at(vid as usize)
155 .unwrap_or(super::super::ColdStamps::dead_gap());
156 if hot.edge_id == INVALID_EDGE_ID || cold.is_live() {
157 return (0, 0);
158 }
159 let reclaimable =
160 usize::from(cutoff != Timestamp::MAX && is_reclaimable_cold(&cold, cutoff));
161 (1, reclaimable)
162 }
163
164 pub fn compact_vertex_with_reporting(
165 &mut self,
166 vid: u32,
167 cutoff: Timestamp,
168 on_edge_removed: &mut dyn FnMut(EdgeId, Timestamp),
169 ) -> usize {
170 if self.reclaimable_count(vid, cutoff) == 0 {
171 return 0;
172 }
173 let src_idx = vid as usize;
174 let edge_id = self
175 .hot_at(src_idx)
176 .map(|h| h.edge_id)
177 .unwrap_or(INVALID_EDGE_ID);
178 let delete_ts = self
179 .cold_at(src_idx)
180 .map(|c| c.delete_ts)
181 .unwrap_or(Timestamp::MAX);
182 on_edge_removed(edge_id, delete_ts);
183 self.set_slot(src_idx, empty_slot());
184 1
185 }
186
187 pub fn compact_with_ts_reporting(
188 &mut self,
189 cutoff: Timestamp,
190 on_edge_removed: &mut dyn FnMut(EdgeId, Timestamp),
191 ) -> usize {
192 if cutoff == Timestamp::MAX {
193 return 0;
194 }
195 let mut reclaimable: Vec<(EdgeId, Timestamp, usize)> = Vec::new();
196 for seg_idx in 0..self.segments.len() {
197 let Some(segment) = self.segments.get(seg_idx).and_then(|s| s.as_ref()) else {
198 continue;
199 };
200 for off in 0..Self::SEGMENT_ROWS {
201 let hot = segment.hot[off];
202 let cold = segment.cold[off];
203 if hot.edge_id != INVALID_EDGE_ID && is_reclaimable_cold(&cold, cutoff) {
204 let idx = seg_idx * Self::SEGMENT_ROWS + off;
205 reclaimable.push((hot.edge_id, cold.delete_ts, idx));
206 }
207 }
208 }
209 let mut removed = 0usize;
210 for (edge_id, delete_ts, idx) in reclaimable {
211 on_edge_removed(edge_id, delete_ts);
212 self.set_slot(idx, empty_slot());
213 removed += 1;
214 }
215 removed
216 }
217
218 pub fn clear(&mut self) {
219 self.segments.clear();
220 self.present.clear();
221 self.vertex_capacity = 0;
222 self.edge_count = 0;
223 }
224}