1use linkrs_core::types::{EdgeId, EdgeStrategy, Timestamp, VertexId};
10use linkrs_core::{StorageError, StorageResult};
11
12use super::super::{csr_shared::decode_endpoint_pair, CsrBase, EdgePosition, MutableCsrTrait, Nbr};
13use super::{local_vid, CsrShardSet};
14
15impl CsrBase for CsrShardSet {
16 fn vertex_capacity(&self) -> usize {
22 self.shards.len() * self.group_size()
23 }
24
25 fn edge_count(&self) -> u64 {
26 self.shards
27 .values()
28 .map(|shard| shard.variant.edge_count())
29 .sum()
30 }
31
32 fn dump(&self) -> Vec<u8> {
38 panic!("whole-direction shard dump removed: use per-group incremental protocol");
39 }
40
41 fn dump_into(&self, _out: &mut Vec<u8>) {
42 panic!("whole-direction shard dump removed: use per-group incremental protocol");
43 }
44
45 fn load(&mut self, _data: &[u8]) -> StorageResult<()> {
48 Err(StorageError::deserialize_error(
49 "whole-direction shard dump removed: use per-group incremental protocol".to_string(),
50 ))
51 }
52}
53
54fn no_edges_error() -> StorageError {
55 StorageError::invalid_operation(super::super::NO_EDGES_STORED_MSG.to_string())
56}
57
58impl MutableCsrTrait for CsrShardSet {
59 fn insert_edge(
60 &mut self,
61 src_vid: u32,
62 dst: VertexId,
63 edge_id: EdgeId,
64 ts: Timestamp,
65 ) -> StorageResult<()> {
66 let gid = self.ensure_group_for(src_vid)?;
67 let local = local_vid(src_vid, self.group_bits);
68 self.shards
69 .get_mut(&gid)
70 .ok_or_else(|| {
71 StorageError::invalid_operation(format!("missing group {} on insert", gid))
72 })?
73 .variant
74 .insert_edge(local, dst, edge_id, ts)?;
75 let (decoded_endpoint, decoded_rank) = decode_endpoint_pair(dst).ok_or_else(|| {
76 StorageError::invalid_input(format!("Malformed edge endpoint key: {}", dst))
77 })?;
78 let nbr = Nbr::with_create_ts(decoded_endpoint, decoded_rank, edge_id, ts);
79 self.mark_region_insert(gid, local);
80 self.record_append_insert(gid, local, nbr);
81 Ok(())
82 }
83
84 fn delete_edge(&mut self, src_vid: u32, edge_id: EdgeId, ts: Timestamp) -> StorageResult<bool> {
85 let Some((gid, local)) = self.route(src_vid) else {
89 if self.strategy == EdgeStrategy::None {
90 return Err(no_edges_error());
91 }
92 return Ok(false);
93 };
94 let deleted = self
95 .shards
96 .get_mut(&gid)
97 .ok_or_else(no_edges_error)?
98 .variant
99 .delete_edge(local, edge_id, ts)?;
100 if deleted {
101 self.mark_region_delete(gid, local);
102 self.record_append_delete(gid, local, edge_id, ts);
103 if let Some(shard) = self.shards.get_mut(&gid) {
104 shard.reclaim_hint = true;
105 shard.dead_entries = shard.dead_entries.saturating_add(1);
106 }
107 }
108 Ok(deleted)
109 }
110
111 fn delete_edge_by_dst(&mut self, src_vid: u32, dst: VertexId, ts: Timestamp) -> usize {
112 let mut noop = |_: EdgeId| {};
113 self.delete_edge_by_dst_reporting(src_vid, dst, ts, &mut noop)
114 }
115
116 fn delete_edge_by_dst_reporting(
117 &mut self,
118 src_vid: u32,
119 dst: VertexId,
120 ts: Timestamp,
121 on_deleted: &mut dyn FnMut(EdgeId),
122 ) -> usize {
123 let Some((gid, local)) = self.route(src_vid) else {
128 return 0;
129 };
130 let mut doomed: Vec<EdgeId> = Vec::new();
135 let deleted = self
136 .shards
137 .get_mut(&gid)
138 .map(|shard| {
139 shard.variant.delete_edge_by_dst_reporting(
140 local,
141 dst,
142 ts,
143 &mut |edge_id: EdgeId| {
144 on_deleted(edge_id);
145 doomed.push(edge_id);
146 },
147 )
148 })
149 .unwrap_or(0);
150 if deleted > 0 {
151 self.mark_region_delete(gid, local);
152 for edge_id in doomed {
153 self.record_append_delete(gid, local, edge_id, ts);
154 }
155 if let Some(shard) = self.shards.get_mut(&gid) {
156 shard.reclaim_hint = true;
157 shard.dead_entries = shard.dead_entries.saturating_add(deleted);
158 }
159 }
160 deleted
161 }
162
163 fn delete_edge_by_dst_reporting_positioned(
164 &mut self,
165 src_vid: u32,
166 dst: VertexId,
167 ts: Timestamp,
168 on_deleted: &mut dyn FnMut(EdgeId, Option<EdgePosition>),
169 ) -> usize {
170 let Some((gid, local)) = self.route(src_vid) else {
171 return 0;
172 };
173 let mut doomed: Vec<(EdgeId, Option<EdgePosition>)> = Vec::new();
174 let deleted = self
175 .shards
176 .get_mut(&gid)
177 .map(|shard| {
178 shard.variant.delete_edge_by_dst_reporting_positioned(
179 local,
180 dst,
181 ts,
182 &mut |edge_id: EdgeId, position: Option<EdgePosition>| {
183 on_deleted(edge_id, position);
184 doomed.push((edge_id, position));
185 },
186 )
187 })
188 .unwrap_or(0);
189 if deleted > 0 {
190 self.mark_region_delete(gid, local);
191 for (edge_id, _) in doomed {
192 self.record_append_delete(gid, local, edge_id, ts);
193 }
194 if let Some(shard) = self.shards.get_mut(&gid) {
195 shard.reclaim_hint = true;
196 shard.dead_entries = shard.dead_entries.saturating_add(deleted);
197 }
198 }
199 deleted
200 }
201
202 fn locate_edge(&self, src_vid: u32, edge_id: EdgeId) -> Option<(EdgePosition, Nbr)> {
203 let (gid, local) = self.route(src_vid)?;
204 let shard = self.shards.get(&gid)?;
205 if let Some(mapped) = &shard.mapped {
206 return mapped.locate_edge(local, edge_id);
207 }
208 shard.variant.locate_edge(local, edge_id)
209 }
210
211 fn delete_edge_at_position(
212 &mut self,
213 src_vid: u32,
214 position: EdgePosition,
215 expected: EdgeId,
216 ts: Timestamp,
217 ) -> StorageResult<bool> {
218 let Some((gid, local)) = self.route(src_vid) else {
221 if self.strategy == EdgeStrategy::None {
222 return Err(no_edges_error());
223 }
224 return Ok(false);
225 };
226 let deleted = self
227 .shards
228 .get_mut(&gid)
229 .ok_or_else(no_edges_error)?
230 .variant
231 .delete_edge_at_position(local, position, expected, ts)?;
232 if deleted {
233 self.mark_region_delete(gid, local);
234 self.record_append_delete(gid, local, expected, ts);
235 if let Some(shard) = self.shards.get_mut(&gid) {
236 shard.reclaim_hint = true;
237 shard.dead_entries = shard.dead_entries.saturating_add(1);
238 }
239 }
240 Ok(deleted)
241 }
242
243 fn revert_delete_at_position(
244 &mut self,
245 src_vid: u32,
246 position: EdgePosition,
247 expected: EdgeId,
248 ts: Timestamp,
249 ) -> bool {
250 let Some((gid, local)) = self.route(src_vid) else {
251 return false;
252 };
253 let reverted = self
254 .shards
255 .get_mut(&gid)
256 .map(|shard| {
257 shard
258 .variant
259 .revert_delete_at_position(local, position, expected, ts)
260 })
261 .unwrap_or(false);
262 if reverted {
263 if let Some(shard) = self.shards.get_mut(&gid) {
264 shard.dead_entries = shard.dead_entries.saturating_sub(1);
265 if shard.dead_entries == 0 {
266 shard.reclaim_hint = false;
267 }
268 }
269 }
270 reverted
271 }
272
273 fn delete_edge_by_offset(
274 &mut self,
275 src_vid: u32,
276 offset: i32,
277 ts: Timestamp,
278 ) -> StorageResult<bool> {
279 let Some((gid, local)) = self.route(src_vid) else {
282 if self.strategy == EdgeStrategy::None {
283 return Err(no_edges_error());
284 }
285 return Ok(false);
286 };
287 let before = self
288 .shards
289 .get(&gid)
290 .and_then(|shard| shard.variant.nbr_at_offset(local, offset));
291 let deleted = self
292 .shards
293 .get_mut(&gid)
294 .ok_or_else(no_edges_error)?
295 .variant
296 .delete_edge_by_offset(local, offset, ts)?;
297 if deleted {
298 self.mark_region_delete(gid, local);
299 if let Some(nbr) = before {
300 self.record_append_delete(gid, local, nbr.edge_id, ts);
301 }
302 if let Some(shard) = self.shards.get_mut(&gid) {
303 shard.reclaim_hint = true;
304 shard.dead_entries = shard.dead_entries.saturating_add(1);
305 }
306 }
307 Ok(deleted)
308 }
309
310 fn nbr_at_offset(&self, src_vid: u32, offset: i32) -> Option<Nbr> {
311 let (gid, local) = self.route(src_vid)?;
312 let shard = self.shards.get(&gid)?;
313 if let Some(mapped) = &shard.mapped {
314 return mapped.nbr_at_offset(local, offset);
315 }
316 shard.variant.nbr_at_offset(local, offset)
317 }
318
319 fn get_edge_physical(&self, src_vid: u32, dst: VertexId) -> Option<Nbr> {
320 let (gid, local) = self.route(src_vid)?;
321 let shard = self.shards.get(&gid)?;
322 if let Some(mapped) = &shard.mapped {
323 return mapped.get_edge_physical(local, dst);
324 }
325 shard.variant.get_edge_physical(local, dst)
326 }
327
328 fn physical_edges_of(&self, src_vid: u32) -> Vec<Nbr> {
329 let Some((gid, local)) = self.route(src_vid) else {
330 return Vec::new();
331 };
332 self.shards
333 .get(&gid)
334 .map(|shard| {
335 if let Some(mapped) = &shard.mapped {
336 return mapped.physical_edges_of(local);
337 }
338 shard.variant.physical_edges_of(local)
339 })
340 .unwrap_or_default()
341 }
342
343 fn fill_physical_into(&self, src_vid: u32, out: &mut Vec<Nbr>) {
344 CsrShardSet::fill_physical_into(self, src_vid, out)
345 }
346
347 fn has_physical_entries(&self, vid: u32) -> bool {
348 let Some((gid, local)) = self.route(vid) else {
349 return false;
350 };
351 self.shards.get(&gid).is_some_and(|shard| {
352 if let Some(mapped) = &shard.mapped {
353 return mapped.has_physical_entries(local);
354 }
355 shard.variant.has_physical_entries(local)
356 })
357 }
358
359 fn primary_contains(&self, src_vid: u32, edge_id: EdgeId) -> bool {
360 let Some((gid, local)) = self.route(src_vid) else {
361 return false;
362 };
363 self.shards.get(&gid).is_some_and(|shard| {
364 if let Some(mapped) = &shard.mapped {
365 return mapped.primary_contains(local, edge_id);
366 }
367 shard.variant.primary_contains(local, edge_id)
368 })
369 }
370
371 fn revert_delete_by_offset(&mut self, src_vid: u32, offset: i32, ts: Timestamp) -> bool {
372 let Some((gid, local)) = self.route(src_vid) else {
373 return false;
374 };
375 let reverted = self
376 .shards
377 .get_mut(&gid)
378 .map(|shard| shard.variant.revert_delete_by_offset(local, offset, ts))
379 .unwrap_or(false);
380 if reverted {
381 self.mark_region_delete(gid, local);
382 if let Some(shard) = self.shards.get_mut(&gid) {
383 shard.dead_entries = shard.dead_entries.saturating_sub(1);
384 if shard.dead_entries == 0 {
385 shard.reclaim_hint = false;
386 }
387 }
388 }
389 reverted
390 }
391
392 fn rollback_insert(&mut self, src_vid: u32, edge_id: EdgeId) -> bool {
393 let Some((gid, local)) = self.route(src_vid) else {
394 return false;
395 };
396 let removed = self
397 .shards
398 .get_mut(&gid)
399 .map(|shard| shard.variant.rollback_insert(local, edge_id))
400 .unwrap_or(false);
401 if removed {
402 self.mark_region_delete(gid, local);
403 }
404 removed
405 }
406
407 fn revert_delete_by_edge_id(&mut self, src_vid: u32, edge_id: EdgeId, ts: Timestamp) -> bool {
408 let Some((gid, local)) = self.route(src_vid) else {
409 return false;
410 };
411 let reverted = self
412 .shards
413 .get_mut(&gid)
414 .map(|shard| shard.variant.revert_delete_by_edge_id(local, edge_id, ts))
415 .unwrap_or(false);
416 if reverted {
417 self.mark_region_delete(gid, local);
418 if let Some(shard) = self.shards.get_mut(&gid) {
419 shard.dead_entries = shard.dead_entries.saturating_sub(1);
420 if shard.dead_entries == 0 {
421 shard.reclaim_hint = false;
422 }
423 }
424 }
425 reverted
426 }
427
428 fn get_edge(&self, src_vid: u32, dst: VertexId, ts: Timestamp) -> Option<Nbr> {
429 let (gid, local) = self.route(src_vid)?;
430 let shard = self.shards.get(&gid)?;
431 if let Some(mapped) = &shard.mapped {
432 return mapped.get_edge(local, dst, ts);
433 }
434 shard.variant.get_edge(local, dst, ts)
435 }
436
437 fn edges_of(&self, src_vid: u32, ts: Timestamp) -> Vec<Nbr> {
438 let Some((gid, local)) = self.route(src_vid) else {
439 return Vec::new();
440 };
441 self.shards
442 .get(&gid)
443 .map(|shard| {
444 if let Some(mapped) = &shard.mapped {
445 return mapped.edges_of(local, ts);
446 }
447 shard.variant.edges_of(local, ts)
448 })
449 .unwrap_or_default()
450 }
451
452 fn compact_vertex_with_reporting(
453 &mut self,
454 vid: u32,
455 cutoff: Timestamp,
456 on_edge_removed: &mut dyn FnMut(EdgeId, Timestamp),
457 ) -> usize {
458 let Some((gid, local)) = self.route(vid) else {
459 return 0;
460 };
461 let removed = self
462 .shards
463 .get_mut(&gid)
464 .map(|shard| {
465 shard
466 .variant
467 .compact_vertex_with_reporting(local, cutoff, on_edge_removed)
468 })
469 .unwrap_or(0);
470 if removed > 0 {
471 self.mark_region_delete(gid, local);
472 if let Some(shard) = self.shards.get_mut(&gid) {
473 shard.dead_entries = shard.dead_entries.saturating_sub(removed);
474 if shard.dead_entries == 0 {
475 shard.reclaim_hint = false;
476 }
477 }
478 }
479 removed
480 }
481
482 fn reclaimable_count(&self, vid: u32, cutoff: Timestamp) -> usize {
483 let Some((gid, local)) = self.route(vid) else {
484 return 0;
485 };
486 self.shards
487 .get(&gid)
488 .map(|shard| shard.variant.reclaimable_count(local, cutoff))
489 .unwrap_or(0)
490 }
491
492 fn vertex_needs_compact(&self, vid: u32, cutoff: Timestamp) -> bool {
493 let Some((gid, local)) = self.route(vid) else {
494 return false;
495 };
496 self.shards
497 .get(&gid)
498 .is_some_and(|shard| shard.variant.vertex_needs_compact(local, cutoff))
499 }
500
501 fn vertex_census(&self, vid: u32) -> (usize, usize, usize) {
502 let Some((gid, local)) = self.route(vid) else {
503 return (0, 0, 0);
504 };
505 self.shards
506 .get(&gid)
507 .map(|shard| shard.variant.vertex_census(local))
508 .unwrap_or((0, 0, 0))
509 }
510
511 fn vertex_reclaim_probe(&self, vid: u32, cutoff: Timestamp) -> (usize, usize) {
512 let Some((gid, local)) = self.route(vid) else {
513 return (0, 0);
514 };
515 self.shards
516 .get(&gid)
517 .map(|shard| shard.variant.vertex_reclaim_probe(local, cutoff))
518 .unwrap_or((0, 0))
519 }
520
521 fn row_gap(&self, vid: u32) -> usize {
522 let Some((gid, local)) = self.route(vid) else {
523 return 0;
524 };
525 self.shards
526 .get(&gid)
527 .map(|shard| shard.variant.row_gap(local))
528 .unwrap_or(0)
529 }
530
531 fn row_density(&self, vid: u32) -> f32 {
532 let Some((gid, local)) = self.route(vid) else {
533 return 1.0;
534 };
535 self.shards
536 .get(&gid)
537 .map(|shard| shard.variant.row_density(local))
538 .unwrap_or(1.0)
539 }
540
541 fn rebalance_row(&mut self, vid: u32) -> bool {
542 let Some((gid, local)) = self.route(vid) else {
543 return true;
544 };
545 self.shards
546 .get_mut(&gid)
547 .map(|shard| shard.variant.rebalance_row(local))
548 .unwrap_or(true)
549 }
550
551 fn used_memory_size(&self) -> usize {
552 CsrShardSet::used_memory_size(self)
553 }
554}