linkrs_storage/edge/node_group/
dirt.rs1use super::{group_id_for, local_vid, region_id_for_local, CsrShardSet};
9
10#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
15pub struct RegionDirty {
16 pub inserted: bool,
17 pub deleted: bool,
18 pub column_updated: bool,
19}
20
21impl RegionDirty {
22 pub fn is_dirty(self) -> bool {
23 self.inserted || self.deleted
24 }
25
26 pub fn is_column_dirty(self) -> bool {
27 self.column_updated
28 }
29}
30
31#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
38pub struct GroupDirty {
39 pub inserted: bool,
40 pub deleted: bool,
41 pub column_updated: bool,
42}
43
44impl GroupDirty {
45 pub fn is_dirty(self) -> bool {
46 self.inserted || self.deleted
47 }
48
49 pub fn is_column_dirty(self) -> bool {
53 self.column_updated
54 }
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub enum EdgeCheckpointKind {
64 AppendOnly,
65 Rebalance,
66}
67
68impl CsrShardSet {
69 pub fn group_dirty(&self, gid: usize) -> GroupDirty {
71 self.shards
72 .get(&gid)
73 .map(|shard| shard.dirty)
74 .unwrap_or_default()
75 }
76
77 pub fn dirty_group_ids(&self) -> Vec<usize> {
79 self.shards
80 .iter()
81 .filter_map(|(gid, shard)| shard.dirty.is_dirty().then_some(*gid))
82 .collect()
83 }
84
85 pub fn column_dirty_group_ids(&self) -> Vec<usize> {
93 self.shards
94 .iter()
95 .filter_map(|(gid, shard)| shard.dirty.is_column_dirty().then_some(*gid))
96 .collect()
97 }
98
99 pub fn checkpoint_kind(&self) -> EdgeCheckpointKind {
101 let rebalance = self.shards.values().any(|shard| shard.dirty.deleted);
102 if rebalance {
103 EdgeCheckpointKind::Rebalance
104 } else {
105 EdgeCheckpointKind::AppendOnly
106 }
107 }
108
109 pub fn mark_column_updated_for_group(&mut self, gid: usize) {
122 if !self.shards.contains_key(&gid) {
123 let _ = self.ensure_group_id(gid);
124 }
125 if let Some(shard) = self.shards.get_mut(&gid) {
126 shard.dirty.column_updated = true;
127 for region in shard.regions.iter_mut() {
128 region.column_updated = true;
129 }
130 }
131 }
132
133 pub fn mark_column_updated_for(&mut self, vid: u32) {
134 let gid = group_id_for(vid, self.group_bits);
135 let rid = region_id_for_local(local_vid(vid, self.group_bits));
136 if !self.shards.contains_key(&gid) {
137 let _ = self.ensure_group_id(gid);
138 }
139 if let Some(shard) = self.shards.get_mut(&gid) {
140 shard.dirty.column_updated = true;
141 if let Some(region) = shard.regions.get_mut(rid) {
142 region.column_updated = true;
143 }
144 }
145 }
146
147 pub fn clear_group_dirty(&mut self, gid: usize) {
148 if let Some(shard) = self.shards.get_mut(&gid) {
149 shard.dirty = GroupDirty::default();
150 for region in shard.regions.iter_mut() {
151 *region = RegionDirty::default();
152 }
153 }
154 }
155
156 pub fn clear_all_dirty(&mut self) {
157 for shard in self.shards.values_mut() {
158 shard.dirty = GroupDirty::default();
159 for region in shard.regions.iter_mut() {
160 *region = RegionDirty::default();
161 }
162 }
163 }
164
165 pub fn clear_all_column_dirty(&mut self) {
168 for shard in self.shards.values_mut() {
169 shard.dirty.column_updated = false;
170 for region in shard.regions.iter_mut() {
171 region.column_updated = false;
172 }
173 }
174 }
175
176 pub fn mark_all_dirty(&mut self) {
180 for shard in self.shards.values_mut() {
181 shard.dirty = GroupDirty {
182 inserted: true,
183 deleted: true,
184 column_updated: true,
185 };
186 for region in shard.regions.iter_mut() {
187 *region = RegionDirty {
188 inserted: true,
189 deleted: true,
190 column_updated: true,
191 };
192 }
193 }
194 }
195
196 pub fn needs_checkpoint(&self, gid: usize) -> bool {
198 self.group_dirty(gid).is_dirty()
199 }
200
201 pub fn group_needs_rebalance(&self, gid: usize) -> bool {
204 self.group_dirty(gid).deleted
205 }
206
207 pub fn region_dirty(&self, gid: usize, region: usize) -> RegionDirty {
209 self.shards
210 .get(&gid)
211 .and_then(|shard| shard.regions.get(region))
212 .copied()
213 .unwrap_or_default()
214 }
215
216 pub fn dirty_region_ids(&self, gid: usize) -> Vec<usize> {
218 self.shards
219 .get(&gid)
220 .map(|shard| {
221 shard
222 .regions
223 .iter()
224 .enumerate()
225 .filter_map(|(rid, region)| region.is_dirty().then_some(rid))
226 .collect()
227 })
228 .unwrap_or_default()
229 }
230
231 pub fn region_needs_checkpoint(&self, gid: usize, region: usize) -> bool {
233 self.region_dirty(gid, region).is_dirty()
234 }
235
236 pub fn region_needs_rebalance(&self, gid: usize, region: usize) -> bool {
239 self.region_dirty(gid, region).deleted
240 }
241
242 pub fn clear_region_dirty(&mut self, gid: usize, region: usize) {
243 if let Some(shard) = self.shards.get_mut(&gid) {
244 if let Some(slot) = shard.regions.get_mut(region) {
245 *slot = RegionDirty::default();
246 }
247 if shard.regions.iter().all(|r| !r.is_dirty()) {
248 shard.dirty.inserted = false;
249 shard.dirty.deleted = false;
250 } else {
251 shard.dirty.inserted = shard.regions.iter().any(|r| r.inserted);
252 shard.dirty.deleted = shard.regions.iter().any(|r| r.deleted);
253 }
254 }
255 }
256
257 pub(super) fn mark_region_insert(&mut self, gid: usize, local: u32) {
258 if let Some(shard) = self.shards.get_mut(&gid) {
259 shard.dirty.inserted = true;
260 let rid = region_id_for_local(local);
261 if let Some(region) = shard.regions.get_mut(rid) {
262 region.inserted = true;
263 }
264 }
265 }
266
267 pub(super) fn mark_region_delete(&mut self, gid: usize, local: u32) {
268 if let Some(shard) = self.shards.get_mut(&gid) {
269 shard.dirty.deleted = true;
270 let rid = region_id_for_local(local);
271 if let Some(region) = shard.regions.get_mut(rid) {
272 region.deleted = true;
273 }
274 }
275 }
276}