dag/ops.rs
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8//! DAG and Id operations (mostly traits)
9
10use std::sync::Arc;
11
12use futures::StreamExt;
13use futures::TryStreamExt;
14
15use crate::clone::CloneData;
16use crate::dag::MemDag;
17use crate::default_impl;
18use crate::errors::NotFoundError;
19use crate::id::Group;
20use crate::id::Id;
21use crate::id::Vertex;
22pub use crate::iddag::IdDagAlgorithm;
23use crate::set::id_lazy::IdLazySet;
24use crate::set::id_static::IdStaticSet;
25use crate::set::Set;
26use crate::IdList;
27use crate::IdSet;
28use crate::Result;
29use crate::VerLink;
30use crate::VertexListWithOptions;
31
32/// DAG related read-only algorithms.
33#[async_trait::async_trait]
34pub trait DagAlgorithm: Send + Sync {
35 /// Sort a `Set` topologically in descending order.
36 ///
37 /// The returned set should have `dag` and `id_map` hints set to associated
38 /// with this dag or its previous compatible version. For example, if a
39 /// `set` is sorted on another dag but not in this dag, it should be resorted
40 /// using this dag. If a `set` is empty and not associated to the current
41 /// `dag` in its hints, the return value should be a different empty `set`
42 /// that has the `dag` and `id_map` hints set to this dag.
43 async fn sort(&self, set: &Set) -> Result<Set>;
44
45 /// Re-create the graph so it looks better when rendered.
46 async fn beautify(&self, main_branch: Option<Set>) -> Result<MemDag> {
47 default_impl::beautify(self, main_branch).await
48 }
49
50 /// Extract a sub graph containing only specified vertexes.
51 async fn subdag(&self, set: Set) -> Result<MemDag> {
52 default_impl::subdag(self, set).await
53 }
54
55 /// Get ordered parent vertexes.
56 async fn parent_names(&self, name: Vertex) -> Result<Vec<Vertex>>;
57
58 /// Returns a set that covers all vertexes tracked by this DAG.
59 ///
60 /// Does not include VIRTUAL vertexes.
61 async fn all(&self) -> Result<Set>;
62
63 /// Returns a set that covers all vertexes in the master group.
64 async fn master_group(&self) -> Result<Set>;
65
66 /// Returns a set that covers all vertexes in the virtual group.
67 async fn virtual_group(&self) -> Result<Set>;
68
69 /// Calculates all ancestors reachable from any name from the given set.
70 async fn ancestors(&self, set: Set) -> Result<Set>;
71
72 /// Calculates parents of the given set.
73 ///
74 /// Note: Parent order is not preserved. Use [`Dag::parent_names`]
75 /// to preserve order.
76 async fn parents(&self, set: Set) -> Result<Set> {
77 default_impl::parents(self, set).await
78 }
79
80 /// Calculates the n-th first ancestor.
81 async fn first_ancestor_nth(&self, name: Vertex, n: u64) -> Result<Option<Vertex>> {
82 default_impl::first_ancestor_nth(self, name, n).await
83 }
84
85 /// Calculates ancestors but only follows the first parent.
86 async fn first_ancestors(&self, set: Set) -> Result<Set> {
87 default_impl::first_ancestors(self, set).await
88 }
89
90 /// Calculates heads of the given set.
91 async fn heads(&self, set: Set) -> Result<Set> {
92 default_impl::heads(self, set).await
93 }
94
95 /// Calculates children of the given set.
96 async fn children(&self, set: Set) -> Result<Set>;
97
98 /// Calculates roots of the given set.
99 async fn roots(&self, set: Set) -> Result<Set> {
100 default_impl::roots(self, set).await
101 }
102
103 /// Calculates merges of the selected set (vertexes with >=2 parents).
104 async fn merges(&self, set: Set) -> Result<Set> {
105 default_impl::merges(self, set).await
106 }
107
108 /// Calculates one "greatest common ancestor" of the given set.
109 ///
110 /// If there are no common ancestors, return None.
111 /// If there are multiple greatest common ancestors, pick one arbitrarily.
112 /// Use `gca_all` to get all of them.
113 async fn gca_one(&self, set: Set) -> Result<Option<Vertex>> {
114 default_impl::gca_one(self, set).await
115 }
116
117 /// Calculates all "greatest common ancestor"s of the given set.
118 /// `gca_one` is faster if an arbitrary answer is ok.
119 async fn gca_all(&self, set: Set) -> Result<Set> {
120 default_impl::gca_all(self, set).await
121 }
122
123 /// Calculates all common ancestors of the given set.
124 async fn common_ancestors(&self, set: Set) -> Result<Set> {
125 default_impl::common_ancestors(self, set).await
126 }
127
128 /// Tests if `ancestor` is an ancestor of `descendant`.
129 async fn is_ancestor(&self, ancestor: Vertex, descendant: Vertex) -> Result<bool> {
130 default_impl::is_ancestor(self, ancestor, descendant).await
131 }
132
133 /// Calculates "heads" of the ancestors of the given set. That is,
134 /// Find Y, which is the smallest subset of set X, where `ancestors(Y)` is
135 /// `ancestors(X)`.
136 ///
137 /// This is faster than calculating `heads(ancestors(set))` in certain
138 /// implementations like segmented changelog.
139 ///
140 /// This is different from `heads`. In case set contains X and Y, and Y is
141 /// an ancestor of X, but not the immediate ancestor, `heads` will include
142 /// Y while this function won't.
143 async fn heads_ancestors(&self, set: Set) -> Result<Set> {
144 default_impl::heads_ancestors(self, set).await
145 }
146
147 /// Calculates the "dag range" - vertexes reachable from both sides.
148 async fn range(&self, roots: Set, heads: Set) -> Result<Set>;
149
150 /// Calculates `ancestors(reachable) - ancestors(unreachable)`.
151 async fn only(&self, reachable: Set, unreachable: Set) -> Result<Set> {
152 default_impl::only(self, reachable, unreachable).await
153 }
154
155 /// Calculates `ancestors(reachable) - ancestors(unreachable)`, and
156 /// `ancestors(unreachable)`.
157 /// This might be faster in some implementations than calculating `only` and
158 /// `ancestors` separately.
159 async fn only_both(&self, reachable: Set, unreachable: Set) -> Result<(Set, Set)> {
160 default_impl::only_both(self, reachable, unreachable).await
161 }
162
163 /// Calculates the descendants of the given set.
164 async fn descendants(&self, set: Set) -> Result<Set>;
165
166 /// Calculates `roots` that are reachable from `heads` without going
167 /// through other `roots`. For example, given the following graph:
168 ///
169 /// ```plain,ignore
170 /// F
171 /// |\
172 /// C E
173 /// | |
174 /// B D
175 /// |/
176 /// A
177 /// ```
178 ///
179 /// `reachable_roots(roots=[A, B, C], heads=[F])` returns `[A, C]`.
180 /// `B` is not included because it cannot be reached without going
181 /// through another root `C` from `F`. `A` is included because it
182 /// can be reached via `F -> E -> D -> A` that does not go through
183 /// other roots.
184 ///
185 /// The can be calculated as
186 /// `roots & (heads | parents(only(heads, roots & ancestors(heads))))`.
187 /// Actual implementation might have faster paths.
188 ///
189 /// The `roots & ancestors(heads)` portion filters out bogus roots for
190 /// compatibility, if the callsite does not provide bogus roots, it
191 /// could be simplified to just `roots`.
192 async fn reachable_roots(&self, roots: Set, heads: Set) -> Result<Set> {
193 default_impl::reachable_roots(self, roots, heads).await
194 }
195
196 /// Suggest the next place to test during a bisect.
197 ///
198 /// - `(roots, heads)` are either `(good, bad)` or `(bad, good)`.
199 /// - `skip` should be non-lazy.
200 ///
201 /// Return `(vertex_to_bisect_next, untested_set, roots(high::))`.
202 ///
203 /// If `vertex_to_bisect_next` is `None`, the bisect is completed. At this
204 /// time, `roots(heads::)` is the "first good/bad" set. `untested_set`
205 /// is usually empty, or a subset of `skip`.
206 async fn suggest_bisect(
207 &self,
208 roots: Set,
209 heads: Set,
210 skip: Set,
211 ) -> Result<(Option<Vertex>, Set, Set)>;
212
213 /// Vertexes buffered in memory, not yet written to disk.
214 ///
215 /// Does not include VIRTUAL vertexes.
216 async fn dirty(&self) -> Result<Set>;
217
218 /// Returns true if the vertex names might need to be resolved remotely.
219 fn is_vertex_lazy(&self) -> bool;
220
221 /// Get a snapshot of the current graph that can operate separately.
222 ///
223 /// This makes it easier to fight with borrowck.
224 fn dag_snapshot(&self) -> Result<Arc<dyn DagAlgorithm + Send + Sync>>;
225
226 /// Get a snapshot of the `IdDag` that can operate separately.
227 ///
228 /// This is for advanced use-cases. For example, if callsite wants to
229 /// do some graph calculation without network, and control how to
230 /// batch the vertex name lookups precisely.
231 fn id_dag_snapshot(&self) -> Result<Arc<dyn IdDagAlgorithm + Send + Sync>> {
232 Err(crate::errors::BackendError::Generic(format!(
233 "id_dag_snapshot() is not supported for {}",
234 std::any::type_name::<Self>()
235 ))
236 .into())
237 }
238
239 /// Identity of the dag.
240 fn dag_id(&self) -> &str;
241
242 /// Version of the dag. Useful to figure out compatibility between two dags.
243 ///
244 /// For performance, this does not include changes to the VIRTUAL group.
245 fn dag_version(&self) -> &VerLink;
246}
247
248#[async_trait::async_trait]
249pub trait Parents: Send + Sync {
250 async fn parent_names(&self, name: Vertex) -> Result<Vec<Vertex>>;
251
252 /// A hint of a sub-graph for inserting `heads`.
253 ///
254 /// This is used to reduce remote fetches in a lazy graph. The function
255 /// should ideally return a subset of pending vertexes that are confirmed to
256 /// not overlap in the existing (potentially lazy) graph.
257 ///
258 /// The pending roots will be checked first, if a root is unknown locally
259 /// then all its descendants will be considered unknown locally.
260 ///
261 /// The returned graph is only used to optimize network fetches in
262 /// `assign_head`. It is not used to be actually inserted to the graph. So
263 /// returning an empty or "incorrect" graph does not hurt correctness. But
264 /// might hurt performance. Returning a set that contains vertexes that do
265 /// overlap in the existing graph is incorrect.
266 async fn hint_subdag_for_insertion(&self, _heads: &[Vertex]) -> Result<MemDag>;
267}
268
269#[async_trait::async_trait]
270impl Parents for Arc<dyn DagAlgorithm + Send + Sync> {
271 async fn parent_names(&self, name: Vertex) -> Result<Vec<Vertex>> {
272 DagAlgorithm::parent_names(self, name).await
273 }
274
275 async fn hint_subdag_for_insertion(&self, heads: &[Vertex]) -> Result<MemDag> {
276 let scope = self.dirty().await?;
277 default_impl::hint_subdag_for_insertion(self, &scope, heads).await
278 }
279}
280
281#[async_trait::async_trait]
282impl Parents for &(dyn DagAlgorithm + Send + Sync) {
283 async fn parent_names(&self, name: Vertex) -> Result<Vec<Vertex>> {
284 DagAlgorithm::parent_names(*self, name).await
285 }
286
287 async fn hint_subdag_for_insertion(&self, heads: &[Vertex]) -> Result<MemDag> {
288 let scope = self.dirty().await?;
289 default_impl::hint_subdag_for_insertion(self, &scope, heads).await
290 }
291}
292
293#[async_trait::async_trait]
294impl<'a> Parents for Box<dyn Fn(Vertex) -> Result<Vec<Vertex>> + Send + Sync + 'a> {
295 async fn parent_names(&self, name: Vertex) -> Result<Vec<Vertex>> {
296 (self)(name)
297 }
298
299 async fn hint_subdag_for_insertion(&self, _heads: &[Vertex]) -> Result<MemDag> {
300 // No clear way to detect the "dirty" scope.
301 Ok(MemDag::new())
302 }
303}
304
305#[async_trait::async_trait]
306impl Parents for std::collections::HashMap<Vertex, Vec<Vertex>> {
307 async fn parent_names(&self, name: Vertex) -> Result<Vec<Vertex>> {
308 match self.get(&name) {
309 Some(v) => Ok(v.clone()),
310 None => name.not_found(),
311 }
312 }
313
314 async fn hint_subdag_for_insertion(&self, heads: &[Vertex]) -> Result<MemDag> {
315 let mut keys: Vec<Vertex> = self.keys().cloned().collect();
316 keys.sort_unstable();
317 let scope = Set::from_static_names(keys);
318 default_impl::hint_subdag_for_insertion(self, &scope, heads).await
319 }
320}
321
322/// Add vertexes recursively to the DAG.
323#[async_trait::async_trait]
324pub trait DagAddHeads {
325 /// Add non-lazy vertexes and their ancestors in memory.
326 ///
327 /// Does not persist changes to disk. Use `add_heads_and_flush` to persist.
328 /// Use `import_pull_data` to add lazy segments to the DAG.
329 ///
330 /// `heads` must use non-MASTER (NON_MASTER, VIRTUAL) groups as
331 /// `desired_group`. `heads` are imported in the given order.
332 ///
333 /// | Method | Allowed groups | Persist | Lazy |
334 /// |---------------------|-------------------------|---------|------|
335 /// | add_heads | NON_MASTER, VIRTUAL [1] | No | No |
336 /// | add_heads_and_flush | MASTER | Yes | No |
337 /// | import_pull_data | MASTER | Yes | Yes |
338 ///
339 /// [1]: Changes to the VIRTUAL group may not survive reloading. Use
340 /// `set_managed_virtual_group` to "pin" content in VIRTUAL that survives
341 /// reloads.
342 async fn add_heads(
343 &mut self,
344 parents: &dyn Parents,
345 heads: &VertexListWithOptions,
346 ) -> Result<bool>;
347}
348
349/// Remove vertexes and their descendants from the DAG.
350#[async_trait::async_trait]
351pub trait DagStrip {
352 /// Remove the given `set` and their descendants on disk.
353 ///
354 /// Reload and persist changes to disk (with lock) immediately.
355 /// Errors out if pending changes in NON_MASTER were added by `add_heads`.
356 ///
357 /// After strip, the `self` graph might contain new vertexes because of
358 /// the reload.
359 async fn strip(&mut self, set: &Set) -> Result<()>;
360}
361
362/// Import a generated `CloneData` object into an empty DAG.
363#[async_trait::async_trait]
364pub trait DagImportCloneData {
365 /// Updates the DAG using a `CloneData` object.
366 ///
367 /// This predates `import_pull_data`. New logic should use the general
368 /// purpose `import_pull_data` instead. Clone is just a special case of
369 /// pull.
370 async fn import_clone_data(&mut self, clone_data: CloneData<Vertex>) -> Result<()>;
371}
372
373/// Import a generated incremental `CloneData` object into an existing DAG.
374/// Ids in the passed CloneData might not match ids in existing DAG.
375#[async_trait::async_trait]
376pub trait DagImportPullData {
377 /// Imports lazy segments ("name" partially known, "shape" known) on disk.
378 ///
379 /// Reload and persist changes to disk (with lock) immediately.
380 /// Errors out if pending changes in NON_MASTER were added by `add_heads`.
381 /// Errors out if `clone_data` overlaps with the existing graph.
382 ///
383 /// `heads` must use MASTER as `desired_group`. `heads` are imported
384 /// in the given order (useful to distinguish between primary and secondary
385 /// branches, and specify their gaps in the id ranges, to reduce
386 /// fragmentation).
387 ///
388 /// `heads` with `reserve_size > 0` must be passed in even if they
389 /// already exist and are not being added, for the id reservation to work
390 /// correctly.
391 ///
392 /// If `clone_data` includes parts not covered by `heads` and their
393 /// ancestors, those parts will be ignored.
394 async fn import_pull_data(
395 &mut self,
396 clone_data: CloneData<Vertex>,
397 heads: &VertexListWithOptions,
398 ) -> Result<()>;
399}
400
401#[async_trait::async_trait]
402pub trait DagExportCloneData {
403 /// Export `CloneData` for vertexes in the master group.
404 async fn export_clone_data(&self) -> Result<CloneData<Vertex>>;
405}
406
407#[async_trait::async_trait]
408pub trait DagExportPullData {
409 /// Export `CloneData` for vertexes in the given set.
410 /// The set is typically calculated by `only(heads, common)`.
411 async fn export_pull_data(&self, set: &Set) -> Result<CloneData<Vertex>>;
412}
413
414/// Persistent the DAG on disk.
415#[async_trait::async_trait]
416pub trait DagPersistent {
417 /// Write in-memory DAG to disk. This might also pick up changes to
418 /// the DAG by other processes.
419 ///
420 /// Calling `add_heads` followed by `flush` is like calling
421 /// `add_heads_and_flush` with the `master_heads` passed to `flush` concated
422 /// with `heads` from `add_heads`. `add_heads` followed by `flush` is more
423 /// flexible but less performant than `add_heads_and_flush`.
424 async fn flush(&mut self, master_heads: &VertexListWithOptions) -> Result<()>;
425
426 /// Write in-memory IdMap that caches Id <-> Vertex translation from
427 /// remote service to disk.
428 async fn flush_cached_idmap(&self) -> Result<()>;
429
430 /// Add non-lazy vertexes, their ancestors, and vertexes added previously by
431 /// `add_heads` on disk.
432 ///
433 /// Reload and persist changes to disk (with lock) immediately.
434 /// Does not error out if pending changes were added by `add_heads`.
435 ///
436 /// `heads` should not use `VIRTUAL` as `desired_group`. `heads` are
437 /// imported in the given order, followed by `heads` previously added
438 /// by `add_heads`.
439 ///
440 /// `heads` with `reserve_size > 0` must be passed in even if they
441 /// already exist and are not being added, for the id reservation to work
442 /// correctly.
443 ///
444 /// `add_heads_and_flush` is faster than `add_heads`. But `add_heads` can
445 /// be useful for the VIRTUAL group, and when the final group is not yet
446 /// decided (ex. the MASTER group is decided by remotenames info but
447 /// remotenames is not yet known at `add_heads` time).
448 async fn add_heads_and_flush(
449 &mut self,
450 parent_names_func: &dyn Parents,
451 heads: &VertexListWithOptions,
452 ) -> Result<()>;
453
454 /// Import from another (potentially large) DAG. Write to disk immediately.
455 async fn import_and_flush(&mut self, dag: &dyn DagAlgorithm, master_heads: Set) -> Result<()> {
456 let heads = dag.heads(dag.all().await?).await?;
457 let non_master_heads = heads - master_heads.clone();
458 let master_heads: Vec<Vertex> = master_heads.iter().await?.try_collect::<Vec<_>>().await?;
459 let non_master_heads: Vec<Vertex> = non_master_heads
460 .iter()
461 .await?
462 .try_collect::<Vec<_>>()
463 .await?;
464 let heads = VertexListWithOptions::from(master_heads)
465 .with_desired_group(Group::MASTER)
466 .chain(non_master_heads);
467 self.add_heads_and_flush(&dag.dag_snapshot()?, &heads).await
468 }
469}
470
471/// Import ASCII graph to DAG.
472pub trait ImportAscii {
473 /// Import vertexes described in an ASCII graph.
474 fn import_ascii(&mut self, text: &str) -> Result<()> {
475 self.import_ascii_with_heads_and_vertex_fn(text, <Option<&[&str]>>::None, None)
476 }
477
478 /// Import vertexes described in an ASCII graph.
479 /// `heads` optionally specifies the order of heads to insert.
480 /// Useful for testing. Panic if the input is invalid.
481 fn import_ascii_with_heads(
482 &mut self,
483 text: &str,
484 heads: Option<&[impl AsRef<str>]>,
485 ) -> Result<()> {
486 self.import_ascii_with_heads_and_vertex_fn(text, heads, None)
487 }
488
489 /// Import vertexes described in an ASCII graph.
490 /// `vertex_fn` specifies how to generate Vertex from a str from the ASCII graph.
491 /// Useful for testing when we need to generate `HgId` (fixed length) from vertex.
492 fn import_ascii_with_vertex_fn(
493 &mut self,
494 text: &str,
495 vertex_fn: fn(&str) -> Vertex,
496 ) -> Result<()> {
497 self.import_ascii_with_heads_and_vertex_fn(text, <Option<&[&str]>>::None, Some(vertex_fn))
498 }
499
500 /// Import vertexes described in an ASCII graph.
501 /// `heads` optionally specifies the order of heads to insert.
502 /// `vertex_fn` specifies how to generate Vertex from a str from the ASCII graph.
503 ///
504 /// This method is a helper function of other APIs, choose other APIs if possible.
505 fn import_ascii_with_heads_and_vertex_fn(
506 &mut self,
507 text: &str,
508 heads: Option<&[impl AsRef<str>]>,
509 vertex_fn: Option<fn(&str) -> Vertex>,
510 ) -> Result<()>;
511}
512
513/// Lookup vertexes by prefixes.
514#[async_trait::async_trait]
515pub trait PrefixLookup {
516 /// Lookup vertexes by hex prefix.
517 async fn vertexes_by_hex_prefix(&self, hex_prefix: &[u8], limit: usize) -> Result<Vec<Vertex>>;
518}
519
520/// Convert between `Vertex` and `Id`.
521#[async_trait::async_trait]
522pub trait IdConvert: PrefixLookup + Sync {
523 async fn vertex_id(&self, name: Vertex) -> Result<Id>;
524 async fn vertex_id_with_max_group(&self, name: &Vertex, max_group: Group)
525 -> Result<Option<Id>>;
526 async fn vertex_name(&self, id: Id) -> Result<Vertex>;
527 async fn contains_vertex_name(&self, name: &Vertex) -> Result<bool>;
528
529 /// Test if an `id` is present locally. Do not trigger remote fetching.
530 async fn contains_vertex_id_locally(&self, id: &[Id]) -> Result<Vec<bool>>;
531
532 /// Test if an `name` is present locally. Do not trigger remote fetching.
533 async fn contains_vertex_name_locally(&self, name: &[Vertex]) -> Result<Vec<bool>>;
534
535 async fn vertex_id_optional(&self, name: &Vertex) -> Result<Option<Id>> {
536 self.vertex_id_with_max_group(name, Group::MAX).await
537 }
538
539 /// Convert [`Id`]s to [`Vertex`]s in batch.
540 async fn vertex_name_batch(&self, ids: &[Id]) -> Result<Vec<Result<Vertex>>> {
541 // This is not an efficient implementation in an async context.
542 let mut names = Vec::with_capacity(ids.len());
543 for &id in ids {
544 names.push(self.vertex_name(id).await);
545 }
546 Ok(names)
547 }
548
549 /// Convert [`Vertex`]s to [`Id`]s in batch.
550 async fn vertex_id_batch(&self, names: &[Vertex]) -> Result<Vec<Result<Id>>> {
551 // This is not an efficient implementation in an async context.
552 let mut ids = Vec::with_capacity(names.len());
553 for name in names {
554 ids.push(self.vertex_id(name.clone()).await);
555 }
556 Ok(ids)
557 }
558
559 /// Identity of the map.
560 fn map_id(&self) -> &str;
561
562 /// Version of the map. Useful to figure out compatibility between two maps.
563 ///
564 /// For performance, this does not include changes to the VIRTUAL group.
565 fn map_version(&self) -> &VerLink;
566}
567
568/// Integrity check functions.
569#[async_trait::async_trait]
570pub trait CheckIntegrity {
571 /// Verify that universally known `Id`s (parents of merges) are actually
572 /// known locally.
573 ///
574 /// Returns set of `Id`s that should be universally known but missing.
575 /// An empty set means all universally known `Id`s are known locally.
576 ///
577 /// Check `FirstAncestorConstraint::KnownUniversally` for concepts of
578 /// "universally known".
579 async fn check_universal_ids(&self) -> Result<Vec<Id>>;
580
581 /// Check segment properties: no cycles, no overlaps, no gaps etc.
582 /// This is only about the `Id`s, not about the vertex names.
583 ///
584 /// Returns human readable messages about problems.
585 /// No messages indicates there are no problems detected.
586 async fn check_segments(&self) -> Result<Vec<String>>;
587
588 /// Check that the subset of the current graph (ancestors of `heads`)
589 /// is isomorphic with the subset in the `other` graph.
590 ///
591 /// Returns messages about where two graphs are different.
592 /// No messages indicates two graphs are likely isomorphic.
593 ///
594 /// Note: For performance, this function only checks the "shape"
595 /// of the graph, without checking the (potentially lazy) vertex
596 /// names.
597 async fn check_isomorphic_graph(
598 &self,
599 other: &dyn DagAlgorithm,
600 heads: Set,
601 ) -> Result<Vec<String>>;
602}
603
604impl<T> ImportAscii for T
605where
606 T: DagAddHeads,
607{
608 fn import_ascii_with_heads_and_vertex_fn(
609 &mut self,
610 text: &str,
611 heads: Option<&[impl AsRef<str>]>,
612 vertex_fn: Option<fn(&str) -> Vertex>,
613 ) -> Result<()> {
614 let vertex_fn = match vertex_fn {
615 Some(vertex_fn) => vertex_fn,
616 None => |s: &str| Vertex::copy_from(s.as_bytes()),
617 };
618 let parents = drawdag::parse(text);
619 let heads: Vec<_> = match heads {
620 Some(heads) => heads.iter().map(|s| vertex_fn(s.as_ref())).collect(),
621 None => {
622 let mut heads: Vec<_> = parents.keys().map(|s| vertex_fn(s)).collect();
623 heads.sort();
624 heads
625 }
626 };
627
628 let parents: std::collections::HashMap<Vertex, Vec<Vertex>> = parents
629 .iter()
630 .map(|(k, vs)| (vertex_fn(k), vs.iter().map(|v| vertex_fn(v)).collect()))
631 .collect();
632 nonblocking::non_blocking_result(self.add_heads(&parents, &heads[..].into()))?;
633 Ok(())
634 }
635}
636
637#[async_trait::async_trait]
638pub trait ToIdSet {
639 /// Converts [`Set`] to [`IdSet`].
640 async fn to_id_set(&self, set: &Set) -> Result<IdSet>;
641}
642
643pub trait ToSet {
644 /// Converts [`IdSet`] to [`Set`].
645 fn to_set(&self, set: &IdSet) -> Result<Set>;
646
647 /// Converts [`IdList`] to [`Set`].
648 fn id_list_to_set(&self, list: &IdList) -> Result<Set>;
649}
650
651pub trait IdMapSnapshot {
652 /// Get a snapshot of IdMap.
653 fn id_map_snapshot(&self) -> Result<Arc<dyn IdConvert + Send + Sync>>;
654}
655
656/// Describes how to persist state to disk.
657pub trait Persist {
658 /// Return type of `lock()`.
659 type Lock: Send + Sync;
660
661 /// Obtain an exclusive lock for writing.
662 /// This should prevent other writers.
663 fn lock(&mut self) -> Result<Self::Lock>;
664
665 /// Reload from the source of truth. Drop pending changes.
666 ///
667 /// This requires a lock and is usually called before `persist()`.
668 fn reload(&mut self, _lock: &Self::Lock) -> Result<()>;
669
670 /// Write pending changes to the source of truth.
671 ///
672 /// This requires a lock.
673 fn persist(&mut self, _lock: &Self::Lock) -> Result<()>;
674}
675
676/// Address that can be used to open things.
677///
678/// The address type decides the return type of `open`.
679pub trait Open: Clone {
680 type OpenTarget;
681
682 fn open(&self) -> Result<Self::OpenTarget>;
683}
684
685/// Has a tuple version that can be used to test if the data was changed.
686pub trait StorageVersion {
687 /// Version tracked by the underlying low-level storage (ex. indexedlog).
688 /// `(epoch, length)`.
689 /// - If `epoch` is changed, then a non-append-only change has happened,
690 /// all caches should be invalidated.
691 /// - If `length` is increased but `epoch` has changed, then the storage
692 /// layer got an append-only change. Note: the append-only change at
693 /// the storage layer does *not* mean append-only at the commit graph
694 /// layer, since the strip operation that removes commits could be
695 /// implemented by appending special data to the storage layer.
696 fn storage_version(&self) -> (u64, u64);
697}
698
699/// Fallible clone.
700pub trait TryClone {
701 fn try_clone(&self) -> Result<Self>
702 where
703 Self: Sized;
704}
705
706impl<T: Clone> TryClone for T {
707 fn try_clone(&self) -> Result<Self> {
708 Ok(self.clone())
709 }
710}
711
712#[async_trait::async_trait]
713impl<T: IdConvert + IdMapSnapshot> ToIdSet for T {
714 /// Converts [`Set`] to [`IdSet`], which no longer preserves iteration order.
715 async fn to_id_set(&self, set: &Set) -> Result<IdSet> {
716 let version = set.hints().id_map_version();
717
718 // Fast path: extract IdSet from IdStaticSet.
719 if let Some(set) = set.as_any().downcast_ref::<IdStaticSet>() {
720 if None < version && version <= Some(self.map_version()) {
721 tracing::debug!(target: "dag::algo::to_id_set", "{:6?} (fast path)", set);
722 return Ok(set.id_set_losing_order().clone());
723 }
724 }
725
726 // Fast path: flatten to IdStaticSet. This works for UnionSet(...) cases.
727 if let Some(set) = set.specialized_flatten_id() {
728 tracing::debug!(target: "dag::algo::to_id_set", "{:6?} (fast path 2)", set);
729 return Ok(set.id_set_losing_order().clone());
730 }
731
732 // Convert IdLazySet to IdStaticSet. Bypass hash lookups.
733 if let Some(set) = set.as_any().downcast_ref::<IdLazySet>() {
734 if None < version && version <= Some(self.map_version()) {
735 tracing::warn!(target: "dag::algo::to_id_set", "{:6?} (slow path 1)", set);
736 let set: IdStaticSet = set.to_static()?;
737 return Ok(set.id_set_losing_order().clone());
738 }
739 }
740
741 // Slow path: iterate through the set and convert it to a non-lazy
742 // IdSet. Does not bypass hash lookups.
743 let mut spans = IdSet::empty();
744 let mut iter = set.iter().await?.chunks(1 << 17);
745 tracing::warn!(target: "dag::algo::to_id_set", "{:6?} (slow path 2)", set);
746 while let Some(names) = iter.next().await {
747 let names = names.into_iter().collect::<Result<Vec<_>>>()?;
748 let ids = self.vertex_id_batch(&names).await?;
749 for id in ids {
750 spans.push(id?);
751 }
752 }
753 Ok(spans)
754 }
755}
756
757impl IdMapSnapshot for Arc<dyn IdConvert + Send + Sync> {
758 fn id_map_snapshot(&self) -> Result<Arc<dyn IdConvert + Send + Sync>> {
759 Ok(self.clone())
760 }
761}
762
763impl<T: IdMapSnapshot + DagAlgorithm> ToSet for T {
764 /// Converts [`IdSet`] to [`Set`].
765 fn to_set(&self, set: &IdSet) -> Result<Set> {
766 Set::from_id_set_dag(set.clone(), self)
767 }
768
769 fn id_list_to_set(&self, list: &IdList) -> Result<Set> {
770 Set::from_id_list_dag(list.clone(), self)
771 }
772}