1mod catalog;
11mod overlay;
12pub mod storage;
13mod trait_impl;
14
15use std::collections::{BTreeMap, BTreeSet};
16use std::sync::Arc;
17
18use uqa_core::{Edge, Vertex};
19use uqa_storage::{CatalogFacade, GraphEntityFilter, GraphEntityKind, PersistentStorageBackend};
20
21use crate::{
22 graphid_label_id, make_graphid, Direction, GraphLabelInfo, GraphLabelRegistry, GraphStore,
23 GraphStoreError, GraphStoreResult, LabelKind,
24};
25use storage::GraphStorage;
26
27const ID_PAGE_SIZE: usize = 256;
28
29pub struct PersistentGraphStore {
32 storage: Arc<dyn GraphStorage>,
33 resource: Option<Arc<dyn Send + Sync>>,
34}
35
36impl Clone for PersistentGraphStore {
37 fn clone(&self) -> Self {
38 Self {
39 storage: Arc::clone(&self.storage),
40 resource: self.resource.clone(),
41 }
42 }
43}
44
45impl std::fmt::Debug for PersistentGraphStore {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 f.debug_struct("PersistentGraphStore")
48 .finish_non_exhaustive()
49 }
50}
51
52impl PersistentGraphStore {
53 #[must_use]
55 pub fn from_catalog(
56 catalog: Arc<dyn CatalogFacade>,
57 backend: Arc<dyn PersistentStorageBackend>,
58 ) -> Self {
59 Self::from_storage(Arc::new(catalog::CatalogGraphStorage { catalog, backend }))
60 }
61
62 pub fn from_storage(storage: Arc<dyn GraphStorage>) -> Self {
63 Self {
64 storage,
65 resource: None,
66 }
67 }
68
69 pub fn retain_resource(mut self, resource: Arc<dyn Send + Sync>) -> Self {
72 self.resource = Some(resource);
73 self
74 }
75
76 pub fn with_read_snapshot(&self, snapshot: &Self) -> Self {
79 Self::from_storage(Arc::new(overlay::OverlayGraphStorage::new(
80 self.clone(),
81 snapshot.clone(),
82 )))
83 }
84
85 pub fn fork_for_mutation(&self) -> Self {
88 Self {
89 storage: self
90 .storage
91 .fork_overlay()
92 .unwrap_or_else(|| Arc::clone(&self.storage)),
93 resource: self.resource.clone(),
94 }
95 }
96
97 pub fn unmodified_read_snapshot(&self) -> Option<Self> {
100 self.storage.unmodified_read_snapshot()
101 }
102
103 pub(crate) fn transaction_mapped<T, E>(
104 &mut self,
105 operation: impl FnOnce(&mut Self) -> Result<T, E>,
106 map_error: impl Fn(GraphStoreError) -> E,
107 ) -> Result<T, E>
108 where
109 E: std::fmt::Display,
110 {
111 let mut checkpoint = self.storage.begin_write().map_err(&map_error)?;
112 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| operation(self)));
113 match result {
114 Ok(Ok(value)) => match checkpoint.commit() {
115 Ok(()) => Ok(value),
116 Err(error) => {
117 checkpoint.rollback().map_err(|rollback| {
118 map_error(GraphStoreError::Storage(format!(
119 "{error}; rollback failed: {rollback}"
120 )))
121 })?;
122 Err(map_error(error))
123 }
124 },
125 Ok(Err(error)) => {
126 checkpoint.rollback().map_err(|rollback| {
127 map_error(GraphStoreError::Storage(format!(
128 "{error}; rollback failed: {rollback}"
129 )))
130 })?;
131 Err(error)
132 }
133 Err(panic) => {
134 drop(checkpoint);
135 std::panic::resume_unwind(panic)
136 }
137 }
138 }
139
140 fn require_graph(&self, graph: &str) -> GraphStoreResult<()> {
141 if self.storage.has_graph(graph)? {
142 Ok(())
143 } else {
144 Err(GraphStoreError::UnknownGraph(graph.to_owned()))
145 }
146 }
147
148 fn for_each_id(
149 &self,
150 filter: GraphEntityFilter<'_>,
151 mut visit: impl FnMut(u64) -> GraphStoreResult<()>,
152 ) -> GraphStoreResult<()> {
153 let mut after = None;
154 loop {
155 let ids = self.storage.ids(filter, after, ID_PAGE_SIZE)?;
156 if ids.is_empty() {
157 return Ok(());
158 }
159 after = ids.last().copied();
160 for id in ids {
161 visit(id)?;
162 }
163 }
164 }
165
166 fn collect_ids(&self, filter: GraphEntityFilter<'_>) -> GraphStoreResult<BTreeSet<u64>> {
167 let mut ids = BTreeSet::new();
168 self.for_each_id(filter, |id| {
169 ids.insert(id);
170 Ok(())
171 })?;
172 Ok(ids)
173 }
174
175 fn require_vertex(&self, id: u64) -> GraphStoreResult<Vertex> {
176 self.storage.vertex(id)?.ok_or_else(|| {
177 GraphStoreError::CorruptGraph(format!(
178 "graph membership references missing vertex {id}"
179 ))
180 })
181 }
182
183 fn require_edge(&self, id: u64, graph: &str) -> GraphStoreResult<Edge> {
184 let edge = self.storage.edge(id)?.ok_or_else(|| {
185 GraphStoreError::CorruptGraph(format!("graph {graph:?} references missing edge {id}"))
186 })?;
187 for endpoint in [edge.source_id, edge.target_id] {
188 if self
189 .storage
190 .has_membership(GraphEntityKind::Vertex, endpoint, graph)?
191 {
192 if self.storage.vertex(endpoint)?.is_some() {
193 continue;
194 }
195 } else if self
196 .storage
197 .registry(graph)?
198 .dropped_label_ids
199 .contains(&graphid_label_id(endpoint))
200 {
201 continue;
202 }
203 return Err(GraphStoreError::CorruptGraph(format!(
204 "graph {graph:?} edge {id} references missing endpoint {endpoint}"
205 )));
206 }
207 Ok(edge)
208 }
209
210 fn detach_entity(&self, kind: GraphEntityKind, id: u64, graph: &str) -> GraphStoreResult<()> {
211 self.storage.detach(kind, id, graph)?;
212 if self.storage.memberships(kind, id)?.is_empty() {
213 match kind {
214 GraphEntityKind::Vertex => self.storage.delete_vertex(id)?,
215 GraphEntityKind::Edge => self.storage.delete_edge(id)?,
216 }
217 }
218 Ok(())
219 }
220
221 fn reserve_id(&self, kind: GraphEntityKind, id: u64) -> GraphStoreResult<()> {
222 let next = id.checked_add(1).ok_or_else(|| {
223 GraphStoreError::IdExhausted(format!("{} id counter overflow", kind.as_str()))
224 })?;
225 let previous = self.next_counter(kind)?;
226 self.storage.save_counter(kind, next.max(previous))?;
227 Ok(())
228 }
229
230 fn next_counter(&self, kind: GraphEntityKind) -> GraphStoreResult<u64> {
231 if let Some(next) = self.storage.counter(kind)? {
232 return Ok(next);
233 }
234 self.storage
235 .max_id(kind)?
236 .unwrap_or(0)
237 .checked_add(1)
238 .ok_or_else(|| {
239 GraphStoreError::IdExhausted(format!("{} id counter overflow", kind.as_str()))
240 })
241 }
242
243 fn allocate_counter(&mut self, kind: GraphEntityKind) -> GraphStoreResult<u64> {
244 self.transaction(|store| {
245 let id = store.next_counter(kind)?;
246 let next = id.checked_add(1).ok_or_else(|| {
247 GraphStoreError::IdExhausted(format!("{} id counter overflow", kind.as_str()))
248 })?;
249 store.storage.save_counter(kind, next)?;
250 Ok(id)
251 })
252 }
253
254 fn allocate_label_id(
255 &mut self,
256 label: &str,
257 graph: &str,
258 kind: LabelKind,
259 ) -> GraphStoreResult<u64> {
260 self.transaction(|store| {
261 store.require_graph(graph)?;
262 let mut registry = store.storage.registry(graph)?;
263 let label_id = registry.label_id(label, kind)?;
264 let id = make_graphid(label_id, registry.next_sequence(label_id)?)?;
265 store.storage.save_registry(graph, ®istry)?;
266 Ok(id)
267 })
268 }
269
270 pub fn label_registry(&self, graph: &str) -> GraphStoreResult<GraphLabelRegistry> {
271 self.require_graph(graph)?;
272 self.storage.registry(graph)
273 }
274
275 pub fn graph_labels(&self, graph: &str) -> GraphStoreResult<Vec<GraphLabelInfo>> {
276 Ok(self.label_registry(graph)?.labels())
277 }
278
279 pub fn graph_label_kind(
280 &self,
281 graph: &str,
282 label: &str,
283 ) -> GraphStoreResult<Option<LabelKind>> {
284 Ok(self.label_registry(graph)?.label_kind(label))
285 }
286
287 pub fn import_label_registry(
288 &mut self,
289 graph: &str,
290 registry: &GraphLabelRegistry,
291 ) -> GraphStoreResult<()> {
292 self.transaction(|store| {
293 let mut combined = store.label_registry(graph)?;
294 combined.merge(registry);
295 store.storage.save_registry(graph, &combined)
296 })
297 }
298
299 pub fn rebuild_label_registry_from_ids(&mut self, graph: &str) -> GraphStoreResult<()> {
302 self.transaction(|store| {
303 let mut registry = store.label_registry(graph)?;
304 for kind in [GraphEntityKind::Vertex, GraphEntityKind::Edge] {
305 store.for_each_id(GraphEntityFilter::new(kind, Some(graph)), |id| {
306 match kind {
307 GraphEntityKind::Vertex => registry.observe(
308 &store.require_vertex(id)?.label,
309 id,
310 LabelKind::Vertex,
311 ),
312 GraphEntityKind::Edge => registry.observe(
313 &store.require_edge(id, graph)?.label,
314 id,
315 LabelKind::Edge,
316 ),
317 }
318 Ok(())
319 })?;
320 }
321 store.storage.save_registry(graph, ®istry)
322 })
323 }
324
325 pub fn create_label(
326 &mut self,
327 graph: &str,
328 label: &str,
329 kind: LabelKind,
330 ) -> GraphStoreResult<Option<u32>> {
331 self.transaction(|store| {
332 let mut registry = store.label_registry(graph)?;
333 let result = registry.register_label(label, kind)?;
334 if result.is_some() {
335 store.storage.save_registry(graph, ®istry)?;
336 }
337 Ok(result)
338 })
339 }
340
341 pub fn drop_label(
342 &mut self,
343 graph: &str,
344 label: &str,
345 ) -> GraphStoreResult<Option<(u32, LabelKind)>> {
346 self.transaction(|store| {
347 let mut registry = store.label_registry(graph)?;
348 let Some(kind) = registry.label_kind(label) else {
349 return Ok(None);
350 };
351 let default = label == kind.default_label_name();
352 let id = if default {
353 if let Some(dependent) = registry
354 .labels
355 .keys()
356 .find(|name| registry.label_kind(name) == Some(kind))
357 {
358 return Err(GraphStoreError::InvalidMutation(format!(
359 "cannot drop default label {label} while label {dependent} depends on it"
360 )));
361 }
362 kind.default_label_id()
363 } else {
364 *registry.labels.get(label).ok_or_else(|| {
365 GraphStoreError::CorruptGraph(format!(
366 "graph {graph:?} label {label:?} has no registry id"
367 ))
368 })?
369 };
370 let entity_kind = match kind {
371 LabelKind::Vertex => GraphEntityKind::Vertex,
372 LabelKind::Edge => GraphEntityKind::Edge,
373 };
374 let mut filter = GraphEntityFilter::new(entity_kind, Some(graph));
375 if !default {
376 filter.label = Some(label);
377 }
378 store.for_each_id(filter, |entity_id| {
381 if !default || graphid_label_id(entity_id) == id {
382 store.detach_entity(entity_kind, entity_id, graph)?;
383 }
384 Ok(())
385 })?;
386 registry.remove_label(label);
387 store.storage.save_registry(graph, ®istry)?;
388 Ok(Some((id, kind)))
389 })
390 }
391
392 pub fn rename_graph(&mut self, from: &str, to: &str) -> GraphStoreResult<()> {
393 if from == to {
394 return Ok(());
395 }
396 self.transaction(|store| {
397 store.require_graph(from)?;
398 if store.storage.has_graph(to)? {
399 return Err(GraphStoreError::InvalidMutation(format!(
400 "graph {to:?} already exists"
401 )));
402 }
403 store.storage.create_graph(to)?;
404 store
405 .storage
406 .save_registry(to, &store.label_registry(from)?)?;
407 for kind in [GraphEntityKind::Vertex, GraphEntityKind::Edge] {
408 store.for_each_id(GraphEntityFilter::new(kind, Some(from)), |id| {
409 store.storage.attach(kind, id, to)?;
410 store.storage.detach(kind, id, from)
411 })?;
412 }
413 store.storage.delete_graph(from)
414 })
415 }
416
417 fn algebra(
418 &mut self,
419 first: &str,
420 second: Option<&str>,
421 target: &str,
422 keep: impl Fn(bool) -> bool,
423 include_second: bool,
424 ) -> GraphStoreResult<()> {
425 self.transaction(|store| {
426 store.require_graph(first)?;
427 if let Some(second) = second {
428 store.require_graph(second)?;
429 }
430 store.create_graph(target)?;
431 for kind in [GraphEntityKind::Vertex, GraphEntityKind::Edge] {
432 store.for_each_id(GraphEntityFilter::new(kind, Some(first)), |id| {
433 let common = match second {
434 Some(second) => store.storage.has_membership(kind, id, second)?,
435 None => false,
436 };
437 if keep(common) {
438 store.storage.attach(kind, id, target)?;
439 }
440 Ok(())
441 })?;
442 if let Some(second) = second.filter(|_| include_second) {
443 store.for_each_id(GraphEntityFilter::new(kind, Some(second)), |id| {
444 store.storage.attach(kind, id, target)
445 })?;
446 }
447 }
448 let mut registry = store.label_registry(target)?;
449 registry.merge(&store.label_registry(first)?);
450 if let Some(second) = second {
451 registry.merge(&store.label_registry(second)?);
452 }
453 store.storage.save_registry(target, ®istry)
454 })
455 }
456}