use crate::change::{Atom, Change, NewEdge, NewVertex};
use crate::changestore::ChangeStore;
use crate::missing_context::*;
use crate::pristine::*;
use crate::record::InodeUpdate;
use crate::Error;
use std::collections::{HashMap, HashSet};
pub fn apply_change_ws<T: MutTxnT, P: ChangeStore>(
changes: &P,
txn: &mut T,
channel: &mut ChannelRef<T>,
hash: Hash,
workspace: &mut Workspace,
) -> Result<(u64, Merkle), anyhow::Error> {
debug!("apply_change {:?}", hash.to_base32());
workspace.clear();
let mut channel = channel.r.borrow_mut();
let change = changes.get_change(&hash)?;
for &hash in change.dependencies.iter() {
if let Hash::None = hash {
continue;
}
if let Some(int) = txn.get_internal(hash) {
if txn.get_changeset(&channel.changes, int, None).is_some() {
continue;
}
}
return Err((Error::DependencyMissing { hash }).into());
}
let internal = if let Some(p) = txn.get_internal(hash) {
p
} else {
let internal: ChangeId = make_changeid(txn, &hash);
register_change(txn, internal, hash, &change)?;
internal
};
debug!("internal = {:?}", internal);
apply_change_to_channel(txn, &mut channel, internal, &hash, &change, workspace)
}
pub fn apply_change_rec_ws<T: TxnT + MutTxnT, P: ChangeStore>(
changes: &P,
txn: &mut T,
channel: &mut ChannelRef<T>,
hash: Hash,
workspace: &mut Workspace,
deps_only: bool,
) -> Result<(), anyhow::Error> {
debug!("apply_change {:?}", hash.to_base32());
workspace.clear();
let mut channel = channel.r.borrow_mut();
let mut dep_stack = vec![(hash, true, !deps_only)];
let mut visited = HashSet::new();
while let Some((hash, first, actually_apply)) = dep_stack.pop() {
let change = changes.get_change(&hash)?;
if first {
if !visited.insert(hash) {
continue;
}
if let Some(change_id) = txn.get_internal(hash) {
if txn
.get_changeset(&channel.changes, change_id, None)
.is_some()
{
continue;
}
}
dep_stack.push((hash, false, actually_apply));
for &hash in change.dependencies.iter() {
if let Hash::None = hash {
continue;
}
dep_stack.push((hash, true, true))
}
} else if actually_apply {
let applied = if let Some(int) = txn.get_internal(hash) {
txn.get_changeset(&channel.changes, int, None).is_some()
} else {
false
};
if !applied {
let internal = if let Some(p) = txn.get_internal(hash) {
p
} else {
let internal: ChangeId = make_changeid(txn, &hash);
register_change(txn, internal, hash, &change)?;
internal
};
debug!("internal = {:?}", internal);
workspace.clear();
apply_change_to_channel(txn, &mut channel, internal, &hash, &change, workspace)?;
}
}
}
Ok(())
}
pub fn apply_change<T: MutTxnT, P: ChangeStore>(
changes: &P,
txn: &mut T,
channel: &mut ChannelRef<T>,
hash: Hash,
) -> Result<(u64, Merkle), anyhow::Error> {
apply_change_ws(changes, txn, channel, hash, &mut Workspace::new())
}
pub fn apply_change_rec<T: MutTxnT, P: ChangeStore>(
changes: &P,
txn: &mut T,
channel: &mut ChannelRef<T>,
hash: Hash,
deps_only: bool,
) -> Result<(), anyhow::Error> {
apply_change_rec_ws(
changes,
txn,
channel,
hash,
&mut Workspace::new(),
deps_only,
)
}
fn apply_change_to_channel<T: MutTxnT>(
txn: &mut T,
channel: &mut Channel<T>,
change_id: ChangeId,
hash: &Hash,
change: &Change,
ws: &mut Workspace,
) -> Result<(u64, Merkle), anyhow::Error> {
ws.assert_empty();
let n = channel.apply_counter;
let merkle =
if let Some(m) = txn.put_changes(channel, change_id, channel.apply_counter, hash)? {
m
} else {
return Err((Error::ChangeAlreadyOnChannel { hash: *hash }).into());
};
debug!("apply change to channel");
let now = std::time::Instant::now();
for change_ in change.changes.iter() {
debug!("Applying {:?} (1)", change_);
for change_ in change_.iter() {
match *change_ {
Atom::NewVertex(ref n) => put_newvertex(txn, channel, change, ws, change_id, n)?,
Atom::EdgeMap(ref n) => {
for edge in n.edges.iter() {
if !edge.flag.contains(EdgeFlags::DELETED) {
put_newedge(
txn,
channel,
ws,
change_id,
n.inode,
edge,
|_, _, _, _| Ok(true),
)?
}
}
}
}
}
}
for change_ in change.changes.iter() {
debug!("Applying {:?} (2)", change_);
for change_ in change_.iter() {
match *change_ {
Atom::EdgeMap(ref n) => {
for edge in n.edges.iter() {
if edge.flag.contains(EdgeFlags::DELETED) {
put_newedge(
txn,
channel,
ws,
change_id,
n.inode,
edge,
|_, _, _, _| Ok(true),
)?;
crate::missing_context::collect_zombie_context(
txn,
channel,
&mut ws.missing_context,
n.inode,
edge,
change_id,
|h| change.knows(&h),
)?
}
}
}
_ => {}
}
}
}
crate::TIMERS.lock().unwrap().apply += now.elapsed();
clean_obsolete_pseudo_edges(txn, channel, ws)?;
info!("repairing missing contexts");
repair_missing_contexts(txn, channel, ws, change_id, change)?;
repair_cyclic_paths(txn, channel, ws, change_id, change)?;
info!("done applying change");
channel.last_modified = std::time::SystemTime::now()
.duration_since(std::time::SystemTime::UNIX_EPOCH)?
.as_secs();
Ok((n, merkle))
}
pub fn apply_local_change_ws<T: MutTxnT>(
txn: &mut T,
channel: &mut ChannelRef<T>,
change: &Change,
hash: Hash,
inode_updates: &HashMap<usize, InodeUpdate>,
workspace: &mut Workspace,
) -> Result<(u64, Merkle), anyhow::Error> {
let mut channel = channel.r.borrow_mut();
let internal: ChangeId = make_changeid(txn, &hash);
register_change(txn, internal, hash, &change)?;
let n = apply_change_to_channel(txn, &mut channel, internal, &hash, &change, workspace)?;
for (_, update) in inode_updates.iter() {
info!("updating {:?}", update);
update_inode(txn, &channel, internal, update)?;
}
Ok(n)
}
pub fn apply_local_change<T: MutTxnT>(
txn: &mut T,
channel: &mut ChannelRef<T>,
change: &Change,
hash: Hash,
inode_updates: &HashMap<usize, InodeUpdate>,
) -> Result<(u64, Merkle), anyhow::Error> {
apply_local_change_ws(
txn,
channel,
change,
hash,
inode_updates,
&mut Workspace::new(),
)
}
fn update_inode<T: MutTxnT>(
txn: &mut T,
channel: &Channel<T>,
internal: ChangeId,
update: &InodeUpdate,
) -> Result<(), anyhow::Error> {
debug!("update_inode {:?}", update);
match *update {
InodeUpdate::Add { inode, pos, .. } => {
let vertex = Position {
change: internal,
pos,
};
if txn
.get_graph(&channel.graph, vertex.inode_vertex(), None)
.is_some()
{
debug!("Adding inodes: {:?} {:?}", inode, vertex);
put_inodes_with_rev(txn, inode, vertex)?;
} else {
debug!("Not adding inodes: {:?} {:?}", inode, vertex);
}
}
InodeUpdate::Deleted { inode } => {
if let Some(parent) = txn.get_revtree(inode, None).map(|x| x.to_owned()) {
del_tree_with_rev(txn, parent.as_file_id(), inode)?;
}
txn.del_tree(
(OwnedPathId {
parent_inode: inode,
basename: crate::small_string::SmallString::new(),
})
.as_file_id(),
Some(inode),
)?;
if let Some(vertex) = txn.get_inodes(inode, None) {
del_inodes_with_rev(txn, inode, vertex)?;
}
}
}
Ok(())
}
fn put_newvertex<T: MutTxnT>(
txn: &mut T,
channel: &mut Channel<T>,
ch: &Change,
ws: &mut Workspace,
change: ChangeId,
n: &NewVertex<Option<Hash>>,
) -> Result<(), anyhow::Error> {
let vertex = Vertex {
change,
start: n.start,
end: n.end,
};
debug!(
"put_newvertex {:?} {:?} {:?} {:?} {:?}",
vertex, n.up_context, n.down_context, n.flag, change
);
assert!(ws.deleted_by.is_empty());
for up in n.up_context.iter() {
let up = internal_pos(txn, up, change)?;
put_up_context(txn, channel, ch, n.inode, ws, up)?;
}
for down in n.down_context.iter() {
let down = internal_pos(txn, down, change)?;
put_down_context(txn, channel, ch, n.inode, ws, down)?;
}
debug!("deleted by: {:?}", ws.deleted_by);
for up in ws.up_context.drain(..) {
assert_ne!(up, vertex);
if !n.flag.contains(EdgeFlags::FOLDER) {
for (change, _) in ws.deleted_by.iter() {
let flag = n.flag | EdgeFlags::BLOCK | EdgeFlags::DELETED;
put_graph_with_rev(txn, channel, flag, up, vertex, *change)?;
}
}
debug!("put_graph_with_rev {:?} {:?}", up, vertex);
put_graph_with_rev(txn, channel, n.flag | EdgeFlags::BLOCK, up, vertex, change)?;
}
debug!("down_context {:?}", ws.down_context);
for down in ws.down_context.drain(..) {
assert_ne!(down, vertex);
if n.flag.contains(EdgeFlags::FOLDER) {
put_graph_with_rev(
txn,
channel,
n.flag | EdgeFlags::BLOCK,
vertex,
down,
change,
)?;
} else {
put_graph_with_rev(
txn,
channel,
n.flag - EdgeFlags::BLOCK,
vertex,
down,
change,
)?;
}
}
ws.deleted_by.clear();
Ok(())
}
fn put_up_context<T: MutTxnT>(
txn: &mut T,
channel: &mut Channel<T>,
ch: &Change,
inode: Position<Option<Hash>>,
ws: &mut Workspace,
up: Position<ChangeId>,
) -> Result<(), anyhow::Error> {
let up_vertex = if up.change.is_root() {
Vertex::ROOT
} else {
debug!("put_up_context {:?}", up);
let k = find_block_end(txn, channel, up)?;
assert_eq!(k.change, up.change);
assert!(k.start <= up.pos);
debug!("k = {:?}", k);
if k.start < up.pos && k.end > up.pos {
if let Some((_, vids)) = ws.missing_context.graphs.get_mut(&inode) {
if let Some(vid) = vids.remove(&k) {
vids.insert(Vertex { end: up.pos, ..k }, vid);
vids.insert(Vertex { start: up.pos, ..k }, vid);
}
}
split_block(txn, channel, k, up.pos)?
}
Vertex {
change: k.change,
start: k.start,
end: up.pos,
}
};
debug!("up_vertex {:?}", up_vertex);
let flag0 = EdgeFlags::PARENT | EdgeFlags::BLOCK;
let flag1 = flag0 | EdgeFlags::DELETED | EdgeFlags::FOLDER;
for parent in iter_adjacent(txn, &channel, up_vertex, flag0, flag1) {
if parent
.flag
.contains(EdgeFlags::PARENT | EdgeFlags::DELETED | EdgeFlags::BLOCK)
{
let introduced_by = txn.get_external(parent.introduced_by).unwrap();
if !ch.knows(&introduced_by) {
ws.deleted_by
.insert((parent.introduced_by, parent.flag - EdgeFlags::PARENT));
}
} else if parent.flag.contains(EdgeFlags::PARENT | EdgeFlags::BLOCK) {
debug!("up_context: alive {:?} {:?}", up_vertex, parent);
break;
}
}
ws.up_context.push(up_vertex);
Ok(())
}
fn put_down_context<T: MutTxnT>(
txn: &mut T,
channel: &mut Channel<T>,
ch: &Change,
inode: Position<Option<Hash>>,
ws: &mut Workspace,
down: Position<ChangeId>,
) -> Result<Vertex<ChangeId>, anyhow::Error> {
let k = find_block(txn, &channel, down)?;
assert_eq!(k.change, down.change);
assert!(k.end >= down.pos);
if k.start < down.pos && k.end > down.pos {
if let Some((_, vids)) = ws.missing_context.graphs.get_mut(&inode) {
if let Some(vid) = vids.remove(&k) {
vids.insert(Vertex { end: down.pos, ..k }, vid);
vids.insert(
Vertex {
start: down.pos,
..k
},
vid,
);
}
}
split_block(txn, channel, k, down.pos)?
}
let down_vertex = Vertex {
change: k.change,
start: down.pos,
end: k.end,
};
debug!("down_vertex {:?}", down_vertex);
let flag0 = EdgeFlags::PARENT;
let flag1 = flag0 | EdgeFlags::FOLDER | EdgeFlags::BLOCK | EdgeFlags::DELETED;
for parent in iter_adjacent(txn, &channel, down_vertex, flag0, flag1) {
if parent.flag.contains(EdgeFlags::PARENT | EdgeFlags::BLOCK) {
if parent.flag.contains(EdgeFlags::DELETED) {
let introduced_by = txn.get_external(parent.introduced_by).unwrap();
if !ch.knows(&introduced_by) {
ws.deleted_by
.insert((parent.introduced_by, parent.flag - EdgeFlags::PARENT));
}
} else {
break;
}
}
}
ws.down_context.push(down_vertex);
Ok(down_vertex)
}
pub struct Workspace {
parents: HashSet<Vertex<ChangeId>>,
children: HashSet<Vertex<ChangeId>>,
pseudo: Vec<(Vertex<ChangeId>, Edge, Position<Option<Hash>>)>,
deleted_by: HashSet<(ChangeId, EdgeFlags)>,
up_context: Vec<Vertex<ChangeId>>,
down_context: Vec<Vertex<ChangeId>>,
pub(crate) missing_context: crate::missing_context::Workspace,
rooted: HashMap<Vertex<ChangeId>, bool>,
}
impl Workspace {
pub fn new() -> Self {
Workspace {
children: HashSet::new(),
parents: HashSet::new(),
pseudo: Vec::new(),
deleted_by: HashSet::new(),
up_context: Vec::new(),
down_context: Vec::new(),
missing_context: crate::missing_context::Workspace::new(),
rooted: HashMap::new(),
}
}
fn clear(&mut self) {
self.children.clear();
self.parents.clear();
self.pseudo.clear();
self.deleted_by.clear();
self.up_context.clear();
self.down_context.clear();
self.missing_context.clear();
self.rooted.clear();
}
fn assert_empty(&self) {
assert!(self.children.is_empty());
assert!(self.parents.is_empty());
assert!(self.pseudo.is_empty());
assert!(self.deleted_by.is_empty());
assert!(self.up_context.is_empty());
assert!(self.down_context.is_empty());
self.missing_context.assert_empty();
assert!(self.rooted.is_empty())
}
}
pub(crate) fn put_newedge<T, F>(
txn: &mut T,
channel: &mut Channel<T>,
ws: &mut Workspace,
change: ChangeId,
inode: Position<Option<Hash>>,
n: &NewEdge<Option<Hash>>,
apply_check: F,
) -> Result<(), anyhow::Error>
where
T: MutTxnT,
F: Fn(
&mut T,
&mut Channel<T>,
Vertex<ChangeId>,
Vertex<ChangeId>,
) -> Result<bool, anyhow::Error>,
{
if n.flag.contains(EdgeFlags::DELETED) {
ws.missing_context.load_graph(txn, channel, inode)?;
}
assert!(ws.children.is_empty());
assert!(ws.parents.is_empty());
debug!("put_newedge {:?} {:?}", n, change);
let n_introduced_by = if let Some(n) = internal(txn, &n.introduced_by, change) {
n
} else {
return Err(crate::Error::InconsistentChange.into());
};
let mut source = find_source_vertex(txn, channel, &n.from, change, inode, n.flag, ws)?;
let mut target = find_target_vertex(txn, channel, &n.to, change, inode, n.flag, ws)?;
loop {
if target.end > n.to.end {
assert!(!n.flag.contains(EdgeFlags::FOLDER));
if let Some((_, vids)) = ws.missing_context.graphs.get_mut(&inode) {
if let Some(vid) = vids.remove(&target) {
vids.insert(
Vertex {
end: n.to.end,
..target
},
vid,
);
vids.insert(
Vertex {
start: n.to.end,
..target
},
vid,
);
}
}
split_block(txn, channel, target, n.to.end)?;
target.end = n.to.end
}
if n.flag.contains(EdgeFlags::DELETED) {
collect_pseudo_edges(txn, channel, ws, inode, target)?;
if !n.flag.contains(EdgeFlags::FOLDER) {
reconnect_pseudo_edges(txn, channel, inode, ws, target)?;
}
ws.children.clear();
}
debug!("deleting {:?} {:?}", n.previous, n.flag);
let del = del_graph_with_rev(txn, channel, n.previous, source, target, n_introduced_by)?;
debug!("deleted: {:?}", del);
debug!("put_graph {:?} {:?}, intro {:?}", source, target, change);
assert_ne!(source, target);
if apply_check(txn, channel, source, target)? {
put_graph_with_rev(txn, channel, n.flag, source, target, change)?;
}
if target.end >= n.to.end {
assert_eq!(target.end, n.to.end);
break;
}
source = target;
target = find_block(txn, channel, target.end_pos())?;
assert_ne!(source, target);
}
Ok(())
}
fn find_source_vertex<T: MutTxnT>(
txn: &mut T,
channel: &mut Channel<T>,
from: &Position<Option<Hash>>,
change: ChangeId,
inode: Position<Option<Hash>>,
flag: EdgeFlags,
ws: &mut Workspace,
) -> Result<Vertex<ChangeId>, anyhow::Error> {
debug!("find_source_vertex");
let mut source = find_block_end(txn, &channel, internal_pos(txn, &from, change)?)?;
debug!("source = {:?}", source);
if source.start < from.pos && source.end > from.pos {
assert!(!flag.contains(EdgeFlags::FOLDER));
if let Some((_, vids)) = ws.missing_context.graphs.get_mut(&inode) {
if let Some(vid) = vids.remove(&source) {
vids.insert(
Vertex {
end: from.pos,
..source
},
vid,
);
vids.insert(
Vertex {
start: from.pos,
..source
},
vid,
);
}
}
split_block(txn, channel, source, from.pos)?;
source.end = from.pos;
}
Ok(source)
}
fn find_target_vertex<T: MutTxnT>(
txn: &mut T,
channel: &mut Channel<T>,
to: &Vertex<Option<Hash>>,
change: ChangeId,
inode: Position<Option<Hash>>,
flag: EdgeFlags,
ws: &mut Workspace,
) -> Result<Vertex<ChangeId>, anyhow::Error> {
let to_pos = internal_pos(txn, &to.start_pos(), change)?;
debug!("find_target_vertex");
let mut target = find_block(txn, channel, to_pos)?;
debug!("target = {:?}", target);
if target.start < to.start {
assert!(!flag.contains(EdgeFlags::FOLDER));
if let Some((_, vids)) = ws.missing_context.graphs.get_mut(&inode) {
if let Some(vid) = vids.remove(&target) {
vids.insert(
Vertex {
end: to.start,
..target
},
vid,
);
vids.insert(
Vertex {
start: to.start,
..target
},
vid,
);
}
}
split_block(txn, channel, target, to.start)?;
target.start = to.start;
}
Ok(target)
}
fn collect_pseudo_edges<T: MutTxnT>(
txn: &mut T,
channel: &mut Channel<T>,
apply: &mut Workspace,
inode: Position<Option<Hash>>,
v: Vertex<ChangeId>,
) -> Result<(), anyhow::Error> {
for e in iter_adjacent(
txn,
&channel,
v,
EdgeFlags::empty(),
EdgeFlags::all() - EdgeFlags::DELETED,
) {
debug!("collect_pseudo_edges {:?} {:?}", v, e);
if !e.flag.contains(EdgeFlags::FOLDER) {
if e.flag.contains(EdgeFlags::PARENT) {
let p = find_block_end(txn, channel, e.dest)?;
if is_alive(txn, channel, p) {
apply.parents.insert(p);
}
} else {
let p = find_block(txn, channel, e.dest)?;
apply.children.insert(p);
}
}
if e.flag.contains(EdgeFlags::PSEUDO) {
apply.pseudo.push((v, e, inode));
}
}
Ok(())
}
fn reconnect_pseudo_edges<T: MutTxnT>(
txn: &mut T,
channel: &mut Channel<T>,
inode: Position<Option<Hash>>,
ws: &mut Workspace,
target: Vertex<ChangeId>,
) -> Result<(), anyhow::Error> {
debug!(
"reconnect: parents.len() = {:?} to children.len() = {:?}",
ws.parents.len(),
ws.children.len()
);
debug!(
"reconnect: parents = {:#?} to children = {:#?}",
ws.parents, ws.children
);
if ws.parents.is_empty() {
ws.children.clear();
return Ok(());
}
if ws.children.is_empty() {
ws.parents.clear();
return Ok(());
}
let (graph, vids) = if let Some(n) = ws.missing_context.graphs.get(&inode) {
n
} else {
return Err(crate::Error::InconsistentChange.into());
};
crate::alive::remove_redundant_parents(
&graph,
&vids,
&mut ws.parents,
&mut ws.missing_context.covered_parents,
target,
);
for &p in ws.parents.iter() {
ws.missing_context.covered_parents.insert((p, target));
}
crate::alive::remove_redundant_children(&graph, &vids, &mut ws.children, target);
debug!(
"reconnect (nonredundant) {:?} to {:?}",
ws.parents, ws.children
);
for p in ws.parents.drain() {
for c in ws.children.iter() {
if p != *c && is_alive(txn, channel, p) && is_alive(txn, channel, *c) {
put_graph_with_rev(txn, channel, EdgeFlags::PSEUDO, p, *c, ChangeId::ROOT)?;
}
}
}
Ok(())
}
pub(crate) fn clean_obsolete_pseudo_edges<T: MutTxnT>(
txn: &mut T,
channel: &mut Channel<T>,
ws: &mut Workspace,
) -> Result<(), anyhow::Error> {
debug!("pseudo = {:#?}", ws.pseudo);
for (next_vertex, p, inode) in ws.pseudo.drain(..) {
debug!("{:?} {:?}", next_vertex, p);
let (a, b) = if p.flag.contains(EdgeFlags::PARENT) {
if let Ok(dest) = find_block_end(txn, channel, p.dest) {
(dest, next_vertex)
} else {
continue;
}
} else {
if let Ok(dest) = find_block(txn, channel, p.dest) {
(next_vertex, dest)
} else {
continue;
}
};
let a_is_alive = is_alive(txn, channel, a);
let b_is_alive = is_alive(txn, channel, b);
debug!("{:?} {:?}", a_is_alive, b_is_alive);
if a_is_alive {
if !b_is_alive {
debug!("deleting {:?} -> {:?}", a, b);
if del_graph_with_rev(
txn,
channel,
p.flag - EdgeFlags::PARENT,
a,
b,
p.introduced_by,
)? {
crate::missing_context::repair_missing_down_context(
txn,
channel,
&mut ws.missing_context,
inode,
b,
&[a],
)?
}
}
} else if b_is_alive {
debug!("deleting {:?} -> {:?}", a, b);
if del_graph_with_rev(
txn,
channel,
p.flag - EdgeFlags::PARENT,
a,
b,
p.introduced_by,
)? {
crate::missing_context::repair_missing_up_context(
txn,
channel,
&mut ws.missing_context,
inode,
a,
&[b],
p.flag.contains(EdgeFlags::FOLDER),
)?
}
} else {
del_graph_with_rev(
txn,
channel,
p.flag - EdgeFlags::PARENT,
a,
b,
p.introduced_by,
)?;
}
}
Ok(())
}
fn repair_missing_contexts<T: MutTxnT>(
txn: &mut T,
channel: &mut Channel<T>,
ws: &mut Workspace,
change_id: ChangeId,
change: &Change,
) -> Result<(), anyhow::Error> {
let now = std::time::Instant::now();
crate::missing_context::repair_parents_of_deleted(txn, channel, &mut ws.missing_context)?;
for atom in change.changes.iter().flat_map(|r| r.iter()) {
match atom {
Atom::NewVertex(ref n) => {
let vertex = Vertex {
change: change_id,
start: n.start,
end: n.end,
};
debug!("repairing missing context for {:?}", vertex);
for up in n.up_context.iter() {
let up = find_block_end(txn, channel, internal_pos(txn, &up, change_id)?)?;
if !is_alive(txn, channel, up) {
debug!("repairing missing up context {:?} {:?}", up, vertex);
repair_missing_up_context(
txn,
channel,
&mut ws.missing_context,
n.inode,
up,
&[vertex],
n.flag.contains(EdgeFlags::FOLDER),
)?
}
}
if !n.flag.contains(EdgeFlags::FOLDER) {
for down in n.down_context.iter() {
let down = find_block(txn, channel, internal_pos(txn, &down, change_id)?)?;
if iter_adjacent(
txn,
channel,
down,
EdgeFlags::PARENT,
EdgeFlags::all() - EdgeFlags::DELETED,
)
.filter(|e| e.introduced_by != change_id)
.next()
.is_none()
{
debug!("repairing missing down context {:?} {:?}", down, vertex);
repair_missing_down_context(
txn,
channel,
&mut ws.missing_context,
n.inode,
down,
&[vertex],
)?
}
}
}
debug!("done repairing contexts for {:?}", vertex);
}
Atom::EdgeMap(ref n) => {
for e in n.edges.iter() {
assert!(!e.flag.contains(EdgeFlags::PARENT));
if !e.flag.contains(EdgeFlags::DELETED) {
trace!("repairing context nondeleted {:?}", e);
repair_context_nondeleted(
txn,
channel,
&mut ws.missing_context,
n.inode,
change_id,
|h| change.knows(&h),
e,
)?
}
}
}
}
}
for atom in change.changes.iter().flat_map(|r| r.iter()) {
match atom {
Atom::EdgeMap(ref n) => {
for e in n.edges.iter() {
if e.flag.contains(EdgeFlags::DELETED) {
trace!("repairing context deleted {:?}", e);
repair_context_deleted(
txn,
channel,
&mut ws.missing_context,
n.inode,
change_id,
|h| change.knows(&h),
e,
)?
}
}
}
_ => {}
}
}
crate::missing_context::delete_pseudo_edges(txn, channel, &mut ws.missing_context)?;
crate::TIMERS.lock().unwrap().repair_context += now.elapsed();
Ok(())
}
fn repair_cyclic_paths<T: MutTxnT>(
txn: &mut T,
channel: &mut Channel<T>,
ws: &mut Workspace,
change_id: ChangeId,
change: &Change,
) -> Result<(), anyhow::Error> {
let now = std::time::Instant::now();
for atom in change.changes.iter().flat_map(|r| r.iter()) {
match atom {
Atom::EdgeMap(ref n) => {
for e in n.edges.iter() {
assert!(!e.flag.contains(EdgeFlags::PARENT));
if e.flag.contains(EdgeFlags::FOLDER | EdgeFlags::DELETED) {
if e.to.len() > 0 {
repair_edge(txn, channel, e, change_id, ws)?;
}
}
}
}
_ => {}
}
}
crate::TIMERS.lock().unwrap().check_cyclic_paths += now.elapsed();
Ok(())
}
fn repair_edge<T: MutTxnT>(
txn: &mut T,
channel: &mut Channel<T>,
e: &NewEdge<Option<Hash>>,
change_id: ChangeId,
ws: &mut Workspace,
) -> Result<(), anyhow::Error> {
let h = if let Some(h) = e.to.change {
h
} else {
return Ok(());
};
let to = Vertex {
change: if let Some(h) = txn.get_internal(h) {
h
} else {
return Err(crate::Error::InconsistentChange.into());
},
start: e.to.start,
end: e.to.end,
};
let mut unrooted = false;
let f0 = EdgeFlags::FOLDER;
let f1 = EdgeFlags::FOLDER | EdgeFlags::BLOCK | EdgeFlags::PSEUDO;
for ee in iter_adjacent(txn, channel, to, f0, f1) {
if !is_rooted(txn, channel, ee.dest.inode_vertex(), ws)? {
unrooted = true;
}
}
if unrooted {
let from = find_block_end(txn, channel, internal_pos(txn, &e.from, change_id)?)?;
debug!("repairing unrooted: {:?} {:?}", from, to);
put_graph_with_rev(
txn,
channel,
EdgeFlags::FOLDER | EdgeFlags::PSEUDO,
from,
to,
ChangeId::ROOT,
)?;
}
Ok(())
}
fn is_rooted<T: TxnT>(
txn: &T,
channel: &Channel<T>,
v: Vertex<ChangeId>,
ws: &mut Workspace,
) -> Result<bool, crate::Error> {
let ref mut stack = ws.up_context;
stack.clear();
stack.push(v);
let ref mut visited = ws.parents;
visited.clear();
while let Some(to) = stack.pop() {
debug!("is_rooted, pop = {:?}", to);
if to.is_root() {
stack.clear();
for v in visited.drain() {
ws.rooted.insert(v, true);
}
return Ok(true);
}
if !visited.insert(to) {
continue;
}
if let Some(&rooted) = ws.rooted.get(&to) {
if rooted {
for v in visited.drain() {
ws.rooted.insert(v, true);
}
return Ok(true);
} else {
continue;
}
}
let f = EdgeFlags::PARENT | EdgeFlags::FOLDER;
for parent in iter_adjacent(
txn,
channel,
to,
f,
f | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
) {
debug!("is_rooted, parent = {:?}", parent);
let parent = find_block_end(txn, channel, parent.dest)?;
stack.push(parent)
}
}
for v in visited.drain() {
ws.rooted.insert(v, false);
}
Ok(false)
}