Skip to main content

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}