Begin the metadata merge trek

This commit is contained in:
Lucien Greathouse 2019-01-16 17:59:57 -08:00
parent 4cfdc72c00
commit 1d412cf0d5
5 changed files with 92 additions and 40 deletions

View file

@ -1,4 +1,5 @@
use std::{ use std::{
collections::hash_map,
path::{self, Path, PathBuf}, path::{self, Path, PathBuf},
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
}; };
@ -31,6 +32,16 @@ impl<T> PathMap<T> {
self.nodes.get(path).map(|v| &v.value) self.nodes.get(path).map(|v| &v.value)
} }
pub fn get_mut(&mut self, path: &Path) -> Option<&mut T> {
self.nodes.get_mut(path).map(|v| &mut v.value)
}
pub fn entry<'a>(&'a mut self, path: PathBuf) -> Entry<'a, T> {
Entry {
internal: self.nodes.entry(path),
}
}
pub fn insert(&mut self, path: PathBuf, value: T) { pub fn insert(&mut self, path: PathBuf, value: T) {
if let Some(parent_path) = path.parent() { if let Some(parent_path) = path.parent() {
if let Some(parent) = self.nodes.get_mut(parent_path) { if let Some(parent) = self.nodes.get_mut(parent_path) {
@ -106,3 +117,16 @@ impl<T> PathMap<T> {
current_path current_path
} }
} }
pub struct Entry<'a, T> {
internal: hash_map::Entry<'a, PathBuf, PathMapNode<T>>,
}
impl<'a, T> Entry<'a, T> {
pub fn or_insert(self, value: T) -> &'a mut T {
&mut self.internal.or_insert(PathMapNode {
value,
children: HashSet::new(),
}).value
}
}

View file

@ -6,6 +6,7 @@ use std::{
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use serde_derive::{Serialize, Deserialize};
use log::{info, trace}; use log::{info, trace};
use rbx_tree::{RbxTree, RbxId}; use rbx_tree::{RbxTree, RbxId};
@ -22,11 +23,22 @@ const INIT_SCRIPT: &str = "init.lua";
const INIT_SERVER_SCRIPT: &str = "init.server.lua"; const INIT_SERVER_SCRIPT: &str = "init.server.lua";
const INIT_CLIENT_SCRIPT: &str = "init.client.lua"; const INIT_CLIENT_SCRIPT: &str = "init.client.lua";
#[derive(Debug, Serialize, Deserialize)]
pub struct MetadataPerPath {
pub instance_id: RbxId,
pub instance_name: Option<String>,
}
pub struct RbxSession { pub struct RbxSession {
tree: RbxTree, tree: RbxTree,
path_map: PathMap<RbxId>,
// TODO(#105): Change metadata_per_path to PathMap<Vec<MetadataPerPath>> for
// path aliasing.
metadata_per_path: PathMap<MetadataPerPath>,
// TODO: Track map from RbxId to PathBuf to support two-way sync
instance_metadata_map: HashMap<RbxId, InstanceProjectNodeMetadata>, instance_metadata_map: HashMap<RbxId, InstanceProjectNodeMetadata>,
sync_point_names: HashMap<PathBuf, String>,
message_queue: Arc<MessageQueue<InstanceChanges>>, message_queue: Arc<MessageQueue<InstanceChanges>>,
imfs: Arc<Mutex<Imfs>>, imfs: Arc<Mutex<Imfs>>,
} }
@ -37,20 +49,18 @@ impl RbxSession {
imfs: Arc<Mutex<Imfs>>, imfs: Arc<Mutex<Imfs>>,
message_queue: Arc<MessageQueue<InstanceChanges>>, message_queue: Arc<MessageQueue<InstanceChanges>>,
) -> RbxSession { ) -> RbxSession {
let mut sync_point_names = HashMap::new(); let mut metadata_per_path = PathMap::new();
let mut path_map = PathMap::new();
let mut instance_metadata_map = HashMap::new(); let mut instance_metadata_map = HashMap::new();
let tree = { let tree = {
let temp_imfs = imfs.lock().unwrap(); let temp_imfs = imfs.lock().unwrap();
reify_initial_tree(&project, &temp_imfs, &mut path_map, &mut instance_metadata_map, &mut sync_point_names) reify_initial_tree(&project, &temp_imfs, &mut metadata_per_path, &mut instance_metadata_map)
}; };
RbxSession { RbxSession {
tree, tree,
path_map, metadata_per_path,
instance_metadata_map, instance_metadata_map,
sync_point_names,
message_queue, message_queue,
imfs, imfs,
} }
@ -67,8 +77,7 @@ impl RbxSession {
.expect("Path was outside in-memory filesystem roots"); .expect("Path was outside in-memory filesystem roots");
// Find the closest instance in the tree that currently exists // Find the closest instance in the tree that currently exists
let mut path_to_snapshot = self.path_map.descend(root_path, path); let mut path_to_snapshot = self.metadata_per_path.descend(root_path, path);
let &instance_id = self.path_map.get(&path_to_snapshot).unwrap();
// If this is a file that might affect its parent if modified, we // If this is a file that might affect its parent if modified, we
// should snapshot its parent instead. // should snapshot its parent instead.
@ -81,10 +90,16 @@ impl RbxSession {
trace!("Snapshotting path {}", path_to_snapshot.display()); trace!("Snapshotting path {}", path_to_snapshot.display());
let instance_name = self.sync_point_names.get(&path_to_snapshot) let path_metadata = self.metadata_per_path.get(&path_to_snapshot).unwrap();
let instance_id = path_metadata.instance_id;
// If this instance is a sync point, pull its name out of our
// per-path metadata store.
let instance_name = path_metadata.instance_name.as_ref()
.map(|value| Cow::Owned(value.to_owned())); .map(|value| Cow::Owned(value.to_owned()));
let mut snapshot_meta = SnapshotMetadata { let mut snapshot_meta = SnapshotMetadata {
sync_point_names: &mut self.sync_point_names, metadata_per_path: &mut self.metadata_per_path,
}; };
let maybe_snapshot = snapshot_imfs_path(&imfs, &mut snapshot_meta, &path_to_snapshot, instance_name) let maybe_snapshot = snapshot_imfs_path(&imfs, &mut snapshot_meta, &path_to_snapshot, instance_name)
.unwrap_or_else(|_| panic!("Could not generate instance snapshot for path {}", path_to_snapshot.display())); .unwrap_or_else(|_| panic!("Could not generate instance snapshot for path {}", path_to_snapshot.display()));
@ -103,7 +118,7 @@ impl RbxSession {
&mut self.tree, &mut self.tree,
instance_id, instance_id,
&snapshot, &snapshot,
&mut self.path_map, &mut self.metadata_per_path,
&mut self.instance_metadata_map, &mut self.instance_metadata_map,
&mut changes, &mut changes,
); );
@ -144,13 +159,13 @@ impl RbxSession {
pub fn path_removed(&mut self, path: &Path) { pub fn path_removed(&mut self, path: &Path) {
info!("Path removed: {}", path.display()); info!("Path removed: {}", path.display());
self.path_map.remove(path); self.metadata_per_path.remove(path);
self.path_created_or_updated(path); self.path_created_or_updated(path);
} }
pub fn path_renamed(&mut self, from_path: &Path, to_path: &Path) { pub fn path_renamed(&mut self, from_path: &Path, to_path: &Path) {
info!("Path renamed from {} to {}", from_path.display(), to_path.display()); info!("Path renamed from {} to {}", from_path.display(), to_path.display());
self.path_map.remove(from_path); self.metadata_per_path.remove(from_path);
self.path_created_or_updated(from_path); self.path_created_or_updated(from_path);
self.path_created_or_updated(to_path); self.path_created_or_updated(to_path);
} }
@ -163,34 +178,32 @@ impl RbxSession {
self.instance_metadata_map.get(&id) self.instance_metadata_map.get(&id)
} }
pub fn debug_get_path_map(&self) -> &PathMap<RbxId> { pub fn debug_get_metadata_per_path(&self) -> &PathMap<MetadataPerPath> {
&self.path_map &self.metadata_per_path
} }
} }
pub fn construct_oneoff_tree(project: &Project, imfs: &Imfs) -> RbxTree { pub fn construct_oneoff_tree(project: &Project, imfs: &Imfs) -> RbxTree {
let mut path_map = PathMap::new(); let mut metadata_per_path = PathMap::new();
let mut instance_metadata_map = HashMap::new(); let mut instance_metadata_map = HashMap::new();
let mut sync_point_names = HashMap::new(); reify_initial_tree(project, imfs, &mut metadata_per_path, &mut instance_metadata_map)
reify_initial_tree(project, imfs, &mut path_map, &mut instance_metadata_map, &mut sync_point_names)
} }
fn reify_initial_tree( fn reify_initial_tree(
project: &Project, project: &Project,
imfs: &Imfs, imfs: &Imfs,
path_map: &mut PathMap<RbxId>, metadata_per_path: &mut PathMap<MetadataPerPath>,
instance_metadata_map: &mut HashMap<RbxId, InstanceProjectNodeMetadata>, instance_metadata_map: &mut HashMap<RbxId, InstanceProjectNodeMetadata>,
sync_point_names: &mut HashMap<PathBuf, String>,
) -> RbxTree { ) -> RbxTree {
let mut meta = SnapshotMetadata { let mut meta = SnapshotMetadata {
sync_point_names, metadata_per_path,
}; };
let snapshot = snapshot_project_tree(imfs, &mut meta, project) let snapshot = snapshot_project_tree(imfs, &mut meta, project)
.expect("Could not snapshot project tree") .expect("Could not snapshot project tree")
.expect("Project did not produce any instances"); .expect("Project did not produce any instances");
let mut changes = InstanceChanges::default(); let mut changes = InstanceChanges::default();
let tree = reify_root(&snapshot, path_map, instance_metadata_map, &mut changes); let tree = reify_root(&snapshot, metadata_per_path, instance_metadata_map, &mut changes);
tree tree
} }

View file

@ -8,7 +8,7 @@ use std::{
use serde_derive::{Serialize, Deserialize}; use serde_derive::{Serialize, Deserialize};
use maplit::hashmap; use maplit::hashmap;
use rbx_tree::{RbxTree, RbxValue, RbxInstanceProperties}; use rbx_tree::{RbxId, RbxTree, RbxValue, RbxInstanceProperties};
use failure::Fail; use failure::Fail;
use crate::{ use crate::{
@ -28,6 +28,9 @@ use crate::{
RbxSnapshotInstance, RbxSnapshotInstance,
snapshot_from_tree, snapshot_from_tree,
}, },
path_map::PathMap,
// TODO: Move MetadataPerPath into this module?
rbx_session::MetadataPerPath,
}; };
const INIT_MODULE_NAME: &str = "init.lua"; const INIT_MODULE_NAME: &str = "init.lua";
@ -37,7 +40,7 @@ const INIT_CLIENT_NAME: &str = "init.client.lua";
pub type SnapshotResult<'a> = Result<Option<RbxSnapshotInstance<'a>>, SnapshotError>; pub type SnapshotResult<'a> = Result<Option<RbxSnapshotInstance<'a>>, SnapshotError>;
pub struct SnapshotMetadata<'meta> { pub struct SnapshotMetadata<'meta> {
pub sync_point_names: &'meta mut HashMap<PathBuf, String>, pub metadata_per_path: &'meta mut PathMap<MetadataPerPath>,
} }
#[derive(Debug, Fail)] #[derive(Debug, Fail)]
@ -138,7 +141,11 @@ fn snapshot_sync_point_node<'source>(
}; };
// Otherwise, we can log the name of the sync point we just snapshotted. // Otherwise, we can log the name of the sync point we just snapshotted.
metadata.sync_point_names.insert(node.path.to_owned(), snapshot.name.clone().into_owned()); // TODO: Use entry API
metadata.metadata_per_path.insert(node.path.to_owned(), MetadataPerPath {
instance_id: RbxId::new(),
instance_name: Some(snapshot.name.clone().into_owned()),
});
Ok(Some(snapshot)) Ok(Some(snapshot))
} }

View file

@ -11,6 +11,7 @@ use serde_derive::{Serialize, Deserialize};
use crate::{ use crate::{
path_map::PathMap, path_map::PathMap,
rbx_session::MetadataPerPath,
project::InstanceProjectNodeMetadata, project::InstanceProjectNodeMetadata,
}; };
@ -86,7 +87,7 @@ pub fn snapshot_from_tree(tree: &RbxTree, id: RbxId) -> Option<RbxSnapshotInstan
pub fn reify_root( pub fn reify_root(
snapshot: &RbxSnapshotInstance, snapshot: &RbxSnapshotInstance,
path_map: &mut PathMap<RbxId>, metadata_per_path: &mut PathMap<MetadataPerPath>,
instance_metadata_map: &mut HashMap<RbxId, InstanceProjectNodeMetadata>, instance_metadata_map: &mut HashMap<RbxId, InstanceProjectNodeMetadata>,
changes: &mut InstanceChanges, changes: &mut InstanceChanges,
) -> RbxTree { ) -> RbxTree {
@ -95,7 +96,12 @@ pub fn reify_root(
let root_id = tree.get_root_id(); let root_id = tree.get_root_id();
if let Some(source_path) = &snapshot.source_path { if let Some(source_path) = &snapshot.source_path {
path_map.insert(source_path.clone(), root_id); // TODO: Use entry API instead?
// metadata_per_path.entry()
metadata_per_path.insert(source_path.clone(), MetadataPerPath {
instance_id: root_id,
instance_name: None,
});
} }
if let Some(metadata) = &snapshot.metadata { if let Some(metadata) = &snapshot.metadata {
@ -105,7 +111,7 @@ pub fn reify_root(
changes.added.insert(root_id); changes.added.insert(root_id);
for child in &snapshot.children { for child in &snapshot.children {
reify_subtree(child, &mut tree, root_id, path_map, instance_metadata_map, changes); reify_subtree(child, &mut tree, root_id, metadata_per_path, instance_metadata_map, changes);
} }
tree tree
@ -115,7 +121,7 @@ pub fn reify_subtree(
snapshot: &RbxSnapshotInstance, snapshot: &RbxSnapshotInstance,
tree: &mut RbxTree, tree: &mut RbxTree,
parent_id: RbxId, parent_id: RbxId,
path_map: &mut PathMap<RbxId>, metadata_per_path: &mut PathMap<MetadataPerPath>,
instance_metadata_map: &mut HashMap<RbxId, InstanceProjectNodeMetadata>, instance_metadata_map: &mut HashMap<RbxId, InstanceProjectNodeMetadata>,
changes: &mut InstanceChanges, changes: &mut InstanceChanges,
) { ) {
@ -123,7 +129,8 @@ pub fn reify_subtree(
let id = tree.insert_instance(instance, parent_id); let id = tree.insert_instance(instance, parent_id);
if let Some(source_path) = &snapshot.source_path { if let Some(source_path) = &snapshot.source_path {
path_map.insert(source_path.clone(), id); // TODO: use entry API
// metadata_per_path.insert(source_path.clone(), id);
} }
if let Some(metadata) = &snapshot.metadata { if let Some(metadata) = &snapshot.metadata {
@ -133,7 +140,7 @@ pub fn reify_subtree(
changes.added.insert(id); changes.added.insert(id);
for child in &snapshot.children { for child in &snapshot.children {
reify_subtree(child, tree, id, path_map, instance_metadata_map, changes); reify_subtree(child, tree, id, metadata_per_path, instance_metadata_map, changes);
} }
} }
@ -141,12 +148,13 @@ pub fn reconcile_subtree(
tree: &mut RbxTree, tree: &mut RbxTree,
id: RbxId, id: RbxId,
snapshot: &RbxSnapshotInstance, snapshot: &RbxSnapshotInstance,
path_map: &mut PathMap<RbxId>, metadata_per_path: &mut PathMap<MetadataPerPath>,
instance_metadata_map: &mut HashMap<RbxId, InstanceProjectNodeMetadata>, instance_metadata_map: &mut HashMap<RbxId, InstanceProjectNodeMetadata>,
changes: &mut InstanceChanges, changes: &mut InstanceChanges,
) { ) {
if let Some(source_path) = &snapshot.source_path { if let Some(source_path) = &snapshot.source_path {
path_map.insert(source_path.clone(), id); // TODO: use entry API
// metadata_per_path.insert(source_path.clone(), id);
} }
if let Some(metadata) = &snapshot.metadata { if let Some(metadata) = &snapshot.metadata {
@ -157,7 +165,7 @@ pub fn reconcile_subtree(
changes.updated.insert(id); changes.updated.insert(id);
} }
reconcile_instance_children(tree, id, snapshot, path_map, instance_metadata_map, changes); reconcile_instance_children(tree, id, snapshot, metadata_per_path, instance_metadata_map, changes);
} }
fn reify_core(snapshot: &RbxSnapshotInstance) -> RbxInstanceProperties { fn reify_core(snapshot: &RbxSnapshotInstance) -> RbxInstanceProperties {
@ -237,7 +245,7 @@ fn reconcile_instance_children(
tree: &mut RbxTree, tree: &mut RbxTree,
id: RbxId, id: RbxId,
snapshot: &RbxSnapshotInstance, snapshot: &RbxSnapshotInstance,
path_map: &mut PathMap<RbxId>, metadata_per_path: &mut PathMap<MetadataPerPath>,
instance_metadata_map: &mut HashMap<RbxId, InstanceProjectNodeMetadata>, instance_metadata_map: &mut HashMap<RbxId, InstanceProjectNodeMetadata>,
changes: &mut InstanceChanges, changes: &mut InstanceChanges,
) { ) {
@ -290,7 +298,7 @@ fn reconcile_instance_children(
} }
for child_snapshot in &children_to_add { for child_snapshot in &children_to_add {
reify_subtree(child_snapshot, tree, id, path_map, instance_metadata_map, changes); reify_subtree(child_snapshot, tree, id, metadata_per_path, instance_metadata_map, changes);
} }
for child_id in &children_to_remove { for child_id in &children_to_remove {
@ -303,6 +311,6 @@ fn reconcile_instance_children(
} }
for (child_id, child_snapshot) in &children_to_update { for (child_id, child_snapshot) in &children_to_update {
reconcile_subtree(tree, *child_id, child_snapshot, path_map, instance_metadata_map, changes); reconcile_subtree(tree, *child_id, child_snapshot, metadata_per_path, instance_metadata_map, changes);
} }
} }

View file

@ -205,10 +205,10 @@ impl Server {
Response::svg(graphviz_to_svg(&dot_source)) Response::svg(graphviz_to_svg(&dot_source))
}, },
(GET) (/visualize/path_map) => { (GET) (/visualize/path_metadata) => {
let rbx_session = self.session.rbx_session.lock().unwrap(); let rbx_session = self.session.rbx_session.lock().unwrap();
Response::json(&rbx_session.debug_get_path_map()) Response::json(&rbx_session.debug_get_metadata_per_path())
}, },
_ => Response::empty_404() _ => Response::empty_404()