1 use crate::alive::{Graph, VertexId};
2 use crate::change::*;
3 use crate::find_alive::*;
4 use crate::pristine::*;
5 use crate::{HashMap, HashSet};
6 use std::collections::hash_map::Entry;
7 
8 #[derive(Debug, Error)]
9 pub enum MissingError<TxnError: std::error::Error + 'static> {
10     #[error(transparent)]
11     Txn(TxnError),
12     #[error(transparent)]
13     Block(#[from] BlockError<TxnError>),
14     #[error(transparent)]
15     Inconsistent(#[from] InconsistentChange<TxnError>),
16 }
17 
18 impl<T: std::error::Error + 'static> std::convert::From<TxnErr<T>> for MissingError<T> {
from(e: TxnErr<T>) -> Self19     fn from(e: TxnErr<T>) -> Self {
20         MissingError::Txn(e.0)
21     }
22 }
23 
24 impl Workspace {
load_graph<T: GraphTxnT>( &mut self, txn: &T, channel: &T::Graph, inode: Position<Option<Hash>>, ) -> Result< Option<&(Graph, HashMap<Vertex<ChangeId>, VertexId>)>, InconsistentChange<T::GraphError>, >25     pub(crate) fn load_graph<T: GraphTxnT>(
26         &mut self,
27         txn: &T,
28         channel: &T::Graph,
29         inode: Position<Option<Hash>>,
30     ) -> Result<
31         Option<&(Graph, HashMap<Vertex<ChangeId>, VertexId>)>,
32         InconsistentChange<T::GraphError>,
33     > {
34         if let Some(change) = inode.change {
35             match self.graphs.0.entry(inode) {
36                 Entry::Occupied(e) => Ok(Some(e.into_mut())),
37                 Entry::Vacant(v) => {
38                     let pos = Position {
39                         change: if let Some(&i) = txn.get_internal(&change.into())? {
40                             i
41                         } else {
42                             return Err(InconsistentChange::UndeclaredDep);
43                         },
44                         pos: inode.pos,
45                     };
46                     let mut graph = crate::alive::retrieve(txn, channel, pos)?;
47                     graph.tarjan();
48                     let mut ids = HashMap::default();
49                     for (i, l) in graph.lines.iter().enumerate() {
50                         ids.insert(l.vertex, VertexId(i));
51                     }
52                     Ok(Some(v.insert((graph, ids))))
53                 }
54             }
55         } else {
56             Ok(None)
57         }
58     }
59 }
60 
repair_missing_up_context< 'a, T: GraphMutTxnT, I: IntoIterator<Item = &'a Vertex<ChangeId>>, >( txn: &mut T, channel: &mut T::Graph, ws: &mut Workspace, change_id: ChangeId, inode: Position<Option<Hash>>, c: Vertex<ChangeId>, d: I, ) -> Result<(), MissingError<T::GraphError>>61 pub(crate) fn repair_missing_up_context<
62     'a,
63     T: GraphMutTxnT,
64     I: IntoIterator<Item = &'a Vertex<ChangeId>>,
65 >(
66     txn: &mut T,
67     channel: &mut T::Graph,
68     ws: &mut Workspace,
69     change_id: ChangeId,
70     inode: Position<Option<Hash>>,
71     c: Vertex<ChangeId>,
72     d: I,
73 ) -> Result<(), MissingError<T::GraphError>> {
74     let now = std::time::Instant::now();
75     let mut alive = find_alive_up(txn, channel, &mut ws.files, c, change_id)?;
76     crate::TIMERS.lock().unwrap().find_alive += now.elapsed();
77     ws.load_graph(txn, channel, inode)?;
78 
79     debug!("repair_missing_up_context, alive = {:?}", alive);
80     for &d in d {
81         if let Some((graph, vids)) = ws.graphs.0.get(&inode) {
82             crate::alive::remove_redundant_parents(
83                 graph,
84                 vids,
85                 &mut alive,
86                 &mut ws.covered_parents,
87                 d,
88             );
89         }
90         repair_regular_up(txn, channel, &alive, d, EdgeFlags::PSEUDO)?;
91     }
92     Ok(())
93 }
94 
repair_regular_up<T: GraphMutTxnT>( txn: &mut T, channel: &mut T::Graph, alive: &HashSet<Vertex<ChangeId>>, d: Vertex<ChangeId>, flag: EdgeFlags, ) -> Result<(), TxnErr<T::GraphError>>95 fn repair_regular_up<T: GraphMutTxnT>(
96     txn: &mut T,
97     channel: &mut T::Graph,
98     alive: &HashSet<Vertex<ChangeId>>,
99     d: Vertex<ChangeId>,
100     flag: EdgeFlags,
101 ) -> Result<(), TxnErr<T::GraphError>> {
102     for &ancestor in alive.iter() {
103         debug!("put_graph_with_rev {:?} -> {:?}", ancestor, d);
104         if ancestor == d {
105             info!(
106                 "repair_missing_up_context, alive: {:?} == {:?}",
107                 ancestor, d
108             );
109             continue;
110         }
111         debug!("repair_missing_up {:?} {:?}", ancestor, d);
112         put_graph_with_rev(txn, channel, flag, ancestor, d, ChangeId::ROOT)?;
113     }
114     Ok(())
115 }
116 
repair_missing_down_context< 'a, T: GraphMutTxnT, I: IntoIterator<Item = &'a Vertex<ChangeId>>, >( txn: &mut T, channel: &mut T::Graph, ws: &mut Workspace, inode: Position<Option<Hash>>, c: Vertex<ChangeId>, d: I, ) -> Result<(), MissingError<T::GraphError>>117 pub(crate) fn repair_missing_down_context<
118     'a,
119     T: GraphMutTxnT,
120     I: IntoIterator<Item = &'a Vertex<ChangeId>>,
121 >(
122     txn: &mut T,
123     channel: &mut T::Graph,
124     ws: &mut Workspace,
125     inode: Position<Option<Hash>>,
126     c: Vertex<ChangeId>,
127     d: I,
128 ) -> Result<(), MissingError<T::GraphError>> {
129     let now = std::time::Instant::now();
130     let mut alive = find_alive_down(txn, channel, c)?;
131     crate::TIMERS.lock().unwrap().find_alive += now.elapsed();
132     ws.load_graph(txn, channel, inode)?;
133     if let Some((graph, vids)) = ws.graphs.0.get(&inode) {
134         crate::alive::remove_redundant_children(graph, vids, &mut alive, c);
135     }
136 
137     if !alive.is_empty() {
138         debug!("repair_missing_down_context alive = {:#?}", alive);
139     }
140 
141     for &d in d {
142         for &desc in alive.iter() {
143             if d == desc {
144                 info!("repair_missing_down_context, alive: {:?} == {:?}", d, desc);
145                 continue;
146             }
147             debug!("repair_missing_down {:?} {:?}", d, desc);
148             put_graph_with_rev(txn, channel, EdgeFlags::PSEUDO, d, desc, ChangeId::ROOT)?;
149         }
150     }
151     Ok(())
152 }
153 
repair_context_nondeleted<T: GraphMutTxnT>( txn: &mut T, channel: &mut T::Graph, ws: &mut Workspace, inode: Position<Option<Hash>>, change_id: ChangeId, e: &NewEdge<Option<Hash>>, ) -> Result<(), MissingError<T::GraphError>>154 pub(crate) fn repair_context_nondeleted<T: GraphMutTxnT>(
155     txn: &mut T,
156     channel: &mut T::Graph,
157     ws: &mut Workspace,
158     inode: Position<Option<Hash>>,
159     change_id: ChangeId,
160     e: &NewEdge<Option<Hash>>,
161 ) -> Result<(), MissingError<T::GraphError>> {
162     if e.flag.contains(EdgeFlags::FOLDER) {
163         return Ok(());
164     }
165     let source = *txn.find_block_end(&channel, internal_pos(txn, &e.from, change_id)?)?;
166     let target = *txn.find_block(&channel, internal_pos(txn, &e.to.start_pos(), change_id)?)?;
167     repair_missing_up_context(txn, channel, ws, change_id, inode, source, &[target])?;
168     reconnect_target_up(txn, channel, ws, inode, target, change_id)?;
169     if e.flag.contains(EdgeFlags::BLOCK) {
170         repair_missing_down_context(txn, channel, ws, inode, target, &[target])?;
171     } else if is_alive(txn, channel, &source)? {
172         repair_missing_down_context(txn, channel, ws, inode, target, &[source])?;
173     }
174     Ok(())
175 }
176 
reconnect_target_up<T: GraphMutTxnT>( txn: &mut T, channel: &mut T::Graph, ws: &mut Workspace, inode: Position<Option<Hash>>, target: Vertex<ChangeId>, change_id: ChangeId, ) -> Result<(), MissingError<T::GraphError>>177 fn reconnect_target_up<T: GraphMutTxnT>(
178     txn: &mut T,
179     channel: &mut T::Graph,
180     ws: &mut Workspace,
181     inode: Position<Option<Hash>>,
182     target: Vertex<ChangeId>,
183     change_id: ChangeId,
184 ) -> Result<(), MissingError<T::GraphError>> {
185     let mut unknown = HashSet::default();
186     for v in iter_deleted_parents(txn, channel, target)? {
187         let v = v?;
188         if v.dest().change.is_root() || v.introduced_by().is_root() {
189             continue;
190         }
191         if v.introduced_by() == change_id {
192             unknown.clear();
193             break;
194         }
195         // Else change ~v.introduced_by~ is a change we don't know,
196         // since no change can create a conflict with itself.
197         unknown.insert(*txn.find_block_end(channel, v.dest())?);
198     }
199     for up in unknown.drain() {
200         repair_missing_up_context(txn, channel, ws, change_id, inode, up, &[target])?;
201     }
202     Ok(())
203 }
204 
repair_context_deleted<T: GraphMutTxnT, K>( txn: &mut T, channel: &mut T::Graph, ws: &mut Workspace, inode: Position<Option<Hash>>, change_id: ChangeId, mut known: K, e: &NewEdge<Option<Hash>>, ) -> Result<(), MissingError<T::GraphError>> where K: FnMut(Hash) -> bool,205 pub(crate) fn repair_context_deleted<T: GraphMutTxnT, K>(
206     txn: &mut T,
207     channel: &mut T::Graph,
208     ws: &mut Workspace,
209     inode: Position<Option<Hash>>,
210     change_id: ChangeId,
211     mut known: K,
212     e: &NewEdge<Option<Hash>>,
213 ) -> Result<(), MissingError<T::GraphError>>
214 where
215     K: FnMut(Hash) -> bool,
216 {
217     if e.flag.contains(EdgeFlags::FOLDER) {
218         return Ok(());
219     }
220     debug!("repair_context_deleted {:?}", e);
221     let mut pos = internal_pos(txn, &e.to.start_pos(), change_id)?;
222     while let Ok(&dest_vertex) = txn.find_block(&channel, pos) {
223         debug!("repair_context_deleted, dest_vertex = {:?}", dest_vertex);
224         repair_children_of_deleted(txn, channel, ws, inode, &mut known, change_id, dest_vertex)?;
225         if dest_vertex.end < e.to.end {
226             pos.pos = dest_vertex.end
227         } else {
228             break;
229         }
230     }
231     Ok(())
232 }
233 
234 /// If we're deleting a folder edge, there is a possibility that this
235 /// solves a "zombie conflict", by removing the last child of a folder
236 /// that was a zombie, in which case that parent folder can finally
237 /// rest in peace.
238 ///
239 /// This function takes care of this for the entire change, by
240 /// removing all obsolete folder conflict edges.
detect_folder_conflict_resolutions<T: GraphMutTxnT>( txn: &mut T, channel: &mut T::Graph, ws: &mut Workspace, change_id: ChangeId, change: &Change, ) -> Result<(), MissingError<T::GraphError>>241 pub(crate) fn detect_folder_conflict_resolutions<T: GraphMutTxnT>(
242     txn: &mut T,
243     channel: &mut T::Graph,
244     ws: &mut Workspace,
245     change_id: ChangeId,
246     change: &Change,
247 ) -> Result<(), MissingError<T::GraphError>> {
248     for change_ in change.changes.iter() {
249         for change_ in change_.iter() {
250             if let Atom::EdgeMap(ref n) = *change_ {
251                 for edge in n.edges.iter() {
252                     if !edge.flag.contains(EdgeFlags::DELETED) {
253                         continue;
254                     }
255                     detect_folder_conflict_resolution(txn, channel, ws, change_id, &n.inode, edge)?
256                 }
257             }
258         }
259     }
260     Ok(())
261 }
262 
detect_folder_conflict_resolution<T: GraphMutTxnT>( txn: &mut T, channel: &mut T::Graph, ws: &mut Workspace, change_id: ChangeId, inode: &Position<Option<Hash>>, e: &NewEdge<Option<Hash>>, ) -> Result<(), MissingError<T::GraphError>>263 fn detect_folder_conflict_resolution<T: GraphMutTxnT>(
264     txn: &mut T,
265     channel: &mut T::Graph,
266     ws: &mut Workspace,
267     change_id: ChangeId,
268     inode: &Position<Option<Hash>>,
269     e: &NewEdge<Option<Hash>>,
270 ) -> Result<(), MissingError<T::GraphError>> {
271     let mut stack = vec![if e.flag.contains(EdgeFlags::FOLDER) {
272         if e.to.is_empty() {
273             internal_pos(txn, &e.to.start_pos(), change_id)?
274         } else {
275             internal_pos(txn, &e.from, change_id)?
276         }
277     } else {
278         internal_pos(txn, &inode, change_id)?
279     }];
280     let len = ws.pseudo.len();
281     while let Some(pos) = stack.pop() {
282         let dest_vertex = if let Ok(&dest_vertex) = txn.find_block_end(&channel, pos) {
283             if !dest_vertex.is_empty() {
284                 continue;
285             }
286             dest_vertex
287         } else {
288             continue;
289         };
290         // Is `dest_vertex` alive? If so, stop this path.
291         let f0 = EdgeFlags::FOLDER | EdgeFlags::PARENT;
292         let f1 = EdgeFlags::FOLDER | EdgeFlags::PARENT | EdgeFlags::BLOCK;
293         if let Some(e) = iter_adjacent(txn, channel, dest_vertex, f0, f1)?
294             .filter_map(|e| e.ok())
295             .filter(|e| !e.flag().contains(EdgeFlags::PSEUDO))
296             .next()
297         {
298             debug!("is_alive: {:?}", e);
299             continue;
300         }
301         // Does `dest_vertex` have alive or zombie descendants? If
302         // so, stop this path.
303         let f0 = EdgeFlags::empty();
304         let f1 = EdgeFlags::FOLDER | EdgeFlags::BLOCK | EdgeFlags::PSEUDO;
305         if let Some(e) = iter_adjacent(txn, channel, dest_vertex, f0, f1)?.next() {
306             debug!("child is_alive: {:?}", e);
307             continue;
308         }
309         // Else, `dest_vertex` is dead. We should remove its
310         // pseudo-parents.
311         let f = EdgeFlags::FOLDER | EdgeFlags::PARENT | EdgeFlags::PSEUDO;
312         for e in iter_adjacent(txn, channel, dest_vertex, f, f)? {
313             let e = e?;
314             ws.pseudo.push((dest_vertex, *e));
315             let gr = *txn.find_block_end(&channel, e.dest()).unwrap();
316             for e in iter_adjacent(txn, channel, gr, f, f)? {
317                 let e = e?;
318                 ws.pseudo.push((gr, *e));
319                 stack.push(e.dest())
320             }
321         }
322     }
323     // Finally, we were only squatting the `.pseudo` field of the
324     // workspace, since that field is normally used for missing
325     // children, and pseudo-edges from that field are treated in a
326     // special way (by `delete_pseudo_edges` in this module).
327     //
328     // Therefore, we need to delete our folder-pseudo-edges now.
329     for (v, e) in ws.pseudo.drain(len..) {
330         let p = *txn.find_block_end(channel, e.dest())?;
331         del_graph_with_rev(
332             txn,
333             channel,
334             e.flag() - EdgeFlags::PARENT,
335             p,
336             v,
337             e.introduced_by(),
338         )?;
339     }
340     Ok(())
341 }
342 
343 #[derive(Default)]
344 pub struct Workspace {
345     pub(crate) unknown_parents: Vec<(
346         Vertex<ChangeId>,
347         Vertex<ChangeId>,
348         Position<Option<Hash>>,
349         EdgeFlags,
350     )>,
351     unknown: Vec<SerializedEdge>,
352     pub(crate) parents: HashSet<SerializedEdge>,
353     pub(crate) pseudo: Vec<(Vertex<ChangeId>, SerializedEdge)>,
354     repaired: HashSet<Vertex<ChangeId>>,
355     pub(crate) graphs: Graphs,
356     pub(crate) covered_parents: HashSet<(Vertex<ChangeId>, Vertex<ChangeId>)>,
357     pub(crate) files: HashSet<Vertex<ChangeId>>,
358 }
359 
360 #[derive(Debug, Default)]
361 pub(crate) struct Graphs(
362     pub HashMap<Position<Option<Hash>>, (Graph, HashMap<Vertex<ChangeId>, crate::alive::VertexId>)>,
363 );
364 
365 impl Graphs {
get( &self, inode: Position<Option<Hash>>, ) -> Option<&(Graph, HashMap<Vertex<ChangeId>, VertexId>)>366     pub(crate) fn get(
367         &self,
368         inode: Position<Option<Hash>>,
369     ) -> Option<&(Graph, HashMap<Vertex<ChangeId>, VertexId>)> {
370         self.0.get(&inode)
371     }
372 
split( &mut self, inode: Position<Option<Hash>>, vertex: Vertex<ChangeId>, mid: ChangePosition, )373     pub fn split(
374         &mut self,
375         inode: Position<Option<Hash>>,
376         vertex: Vertex<ChangeId>,
377         mid: ChangePosition,
378     ) {
379         if let Some((_, vids)) = self.0.get_mut(&inode) {
380             if let Some(vid) = vids.remove(&vertex) {
381                 vids.insert(Vertex { end: mid, ..vertex }, vid);
382                 vids.insert(
383                     Vertex {
384                         start: mid,
385                         ..vertex
386                     },
387                     vid,
388                 );
389             }
390         }
391     }
392 }
393 
394 impl Workspace {
clear(&mut self)395     pub fn clear(&mut self) {
396         self.unknown.clear();
397         self.unknown_parents.clear();
398         self.pseudo.clear();
399         self.parents.clear();
400         self.graphs.0.clear();
401         self.repaired.clear();
402         self.covered_parents.clear();
403     }
assert_empty(&self)404     pub fn assert_empty(&self) {
405         assert!(self.unknown.is_empty());
406         assert!(self.unknown_parents.is_empty());
407         assert!(self.pseudo.is_empty());
408         assert!(self.parents.is_empty());
409         assert!(self.graphs.0.is_empty());
410         assert!(self.repaired.is_empty());
411         assert!(self.covered_parents.is_empty());
412     }
413 }
414 
collect_unknown_children<T: GraphTxnT, K>( txn: &T, channel: &T::Graph, ws: &mut Workspace, dest_vertex: Vertex<ChangeId>, change_id: ChangeId, known: &mut K, ) -> Result<(), TxnErr<T::GraphError>> where K: FnMut(Hash) -> bool,415 fn collect_unknown_children<T: GraphTxnT, K>(
416     txn: &T,
417     channel: &T::Graph,
418     ws: &mut Workspace,
419     dest_vertex: Vertex<ChangeId>,
420     change_id: ChangeId,
421     known: &mut K,
422 ) -> Result<(), TxnErr<T::GraphError>>
423 where
424     K: FnMut(Hash) -> bool,
425 {
426     for v in iter_alive_children(txn, channel, dest_vertex)? {
427         let v = v?;
428         debug!(
429             "collect_unknown_children dest_vertex = {:?}, v = {:?}",
430             dest_vertex, v
431         );
432         if v.introduced_by() == change_id || v.dest().change.is_root() {
433             continue;
434         }
435         if v.introduced_by().is_root() {
436             ws.pseudo.push((dest_vertex, *v));
437             continue;
438         }
439         let mut not_del_by_change = true;
440         for e in iter_adjacent(
441             txn,
442             channel,
443             dest_vertex,
444             EdgeFlags::PARENT | EdgeFlags::DELETED,
445             EdgeFlags::all(),
446         )? {
447             let e = e?;
448             if e.introduced_by() == v.introduced_by() {
449                 not_del_by_change = false;
450                 break;
451             }
452         }
453         if not_del_by_change {
454             let intro = txn.get_external(&v.introduced_by())?.unwrap().into();
455             if !known(intro) {
456                 ws.unknown.push(*v);
457             }
458         }
459     }
460     Ok(())
461 }
462 
repair_children_of_deleted<T: GraphMutTxnT, K>( txn: &mut T, channel: &mut T::Graph, ws: &mut Workspace, inode: Position<Option<Hash>>, mut known: K, change_id: ChangeId, dest_vertex: Vertex<ChangeId>, ) -> Result<(), MissingError<T::GraphError>> where K: FnMut(Hash) -> bool,463 fn repair_children_of_deleted<T: GraphMutTxnT, K>(
464     txn: &mut T,
465     channel: &mut T::Graph,
466     ws: &mut Workspace,
467     inode: Position<Option<Hash>>,
468     mut known: K,
469     change_id: ChangeId,
470     dest_vertex: Vertex<ChangeId>,
471 ) -> Result<(), MissingError<T::GraphError>>
472 where
473     K: FnMut(Hash) -> bool,
474 {
475     trace!("repair_children_of_deleted {:?}", dest_vertex);
476     collect_unknown_children(txn, channel, ws, dest_vertex, change_id, &mut known)?;
477     let mut unknown = std::mem::replace(&mut ws.unknown, Vec::new());
478     debug!("dest_vertex = {:?}, unknown = {:?}", dest_vertex, unknown);
479     for edge in unknown.drain(..) {
480         let p = *txn.find_block(channel, edge.dest())?;
481         assert!(!edge.flag().contains(EdgeFlags::FOLDER));
482         debug!("dest_vertex {:?}, p {:?}", dest_vertex, p);
483         put_graph_with_rev(txn, channel, EdgeFlags::db(), dest_vertex, p, change_id)?;
484         let mut u = p;
485         while let Ok(&v) = txn.find_block(channel, u.end_pos()) {
486             if u != v {
487                 debug!("repair_children_of_deleted: {:?} -> {:?}", u, v);
488                 put_graph_with_rev(txn, channel, EdgeFlags::db(), u, v, change_id)?;
489                 u = v
490             } else {
491                 break;
492             }
493         }
494         if is_alive(txn, channel, &p)? {
495             repair_missing_up_context(txn, channel, ws, change_id, inode, dest_vertex, &[p])?;
496         } else {
497             let alive = find_alive_down(txn, channel, p)?;
498             repair_missing_up_context(txn, channel, ws, change_id, inode, dest_vertex, &alive)?;
499         }
500     }
501     ws.unknown = unknown;
502     Ok(())
503 }
504 
delete_pseudo_edges<T: GraphMutTxnT>( txn: &mut T, channel: &mut T::Graph, ws: &mut Workspace, ) -> Result<(), MissingError<T::GraphError>>505 pub(crate) fn delete_pseudo_edges<T: GraphMutTxnT>(
506     txn: &mut T,
507     channel: &mut T::Graph,
508     ws: &mut Workspace,
509 ) -> Result<(), MissingError<T::GraphError>> {
510     if ws.pseudo.is_empty() {
511         debug!("no pseudo edges")
512     }
513     for (dest_vertex, mut e) in ws.pseudo.drain(..) {
514         debug!("repair_context_deleted, deleting {:?} {:?}", dest_vertex, e);
515         if !is_alive(txn, channel, &dest_vertex)? && !ws.repaired.contains(&dest_vertex) {
516             if e.flag().contains(EdgeFlags::PARENT) {
517                 let p = *txn.find_block_end(channel, e.dest())?;
518                 if !is_alive(txn, channel, &p)? {
519                     debug!("delete {:?} {:?}", p, dest_vertex);
520                     e -= EdgeFlags::PARENT;
521                     del_graph_with_rev(txn, channel, e.flag(), p, dest_vertex, e.introduced_by())?;
522                 }
523             } else {
524                 let p = *txn.find_block(channel, e.dest())?;
525                 if !is_alive(txn, channel, &p)? {
526                     debug!("delete (2) {:?} {:?}", dest_vertex, p);
527                     del_graph_with_rev(txn, channel, e.flag(), dest_vertex, p, e.introduced_by())?;
528                 }
529             }
530         }
531     }
532     Ok(())
533 }
534 
repair_parents_of_deleted<T: GraphMutTxnT>( txn: &mut T, channel: &mut T::Graph, ws: &mut Workspace, ) -> Result<(), MissingError<T::GraphError>>535 pub(crate) fn repair_parents_of_deleted<T: GraphMutTxnT>(
536     txn: &mut T,
537     channel: &mut T::Graph,
538     ws: &mut Workspace,
539 ) -> Result<(), MissingError<T::GraphError>> {
540     debug!("repair_parents_of_deleted");
541     let mut unknown = std::mem::replace(&mut ws.unknown_parents, Vec::new());
542     for (dest_vertex, p, inode, flag) in unknown.drain(..) {
543         if flag.contains(EdgeFlags::FOLDER) {
544             repair_missing_down_context(txn, channel, ws, inode, dest_vertex, &[dest_vertex])?
545         } else {
546             repair_missing_down_context(txn, channel, ws, inode, dest_vertex, &[p])?
547         }
548     }
549     ws.unknown_parents = unknown;
550     Ok(())
551 }
552