Support nested partitions and partitions directly targeting services (#122)

* Do the nested partition thing

* Tidy up touched code

* Add nested partition test project, not fully functional

* Clean up variable names, move path_metadata mutation strictly into snapshot_reconciler

* Remove path_metadata, snapshotting is now pure

* Factor out snapshot metadata storage to fix a missing case

* Pull instance_name out of per_path_metadata, closer to what we need

* Refactor to make metadata make more sense, part one

* All appears to be well

* Cull 'metadata_per_path' in favor of 'instances_per_path'

* Remove SnapshotContext

* InstanceMetadata -> PublicInstanceMetadata in web module

* Build in snapshot testing system for testing... snapshots?

* Remove pretty_assertions to see if it fixes a snapshot comparison bug

* Reintroduce pretty assertions, it's not the cause of inequality

* Fix snapshot tests with custom relative path serializer
This commit is contained in:
Lucien Greathouse
2019-02-07 14:55:01 -08:00
committed by GitHub
parent 38e3c198f2
commit ecb9b5e28f
37 changed files with 999 additions and 396 deletions

View File

@@ -1,6 +1,6 @@
use std::{
borrow::Cow,
collections::HashMap,
collections::{HashSet, HashMap},
path::{Path, PathBuf},
str,
sync::{Arc, Mutex},
@@ -11,11 +11,11 @@ use log::{info, trace};
use rbx_tree::{RbxTree, RbxId};
use crate::{
project::Project,
project::{Project, ProjectNode},
message_queue::MessageQueue,
imfs::{Imfs, ImfsItem},
path_map::PathMap,
rbx_snapshot::{SnapshotContext, snapshot_project_tree, snapshot_imfs_path},
rbx_snapshot::{snapshot_project_tree, snapshot_project_node, snapshot_imfs_path},
snapshot_reconciler::{InstanceChanges, reify_root, reconcile_subtree},
};
@@ -23,24 +23,28 @@ const INIT_SCRIPT: &str = "init.lua";
const INIT_SERVER_SCRIPT: &str = "init.server.lua";
const INIT_CLIENT_SCRIPT: &str = "init.client.lua";
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct MetadataPerPath {
pub instance_id: Option<RbxId>,
pub instance_name: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
/// `source_path` or `project_definition` or both must both be Some.
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct MetadataPerInstance {
pub source_path: Option<PathBuf>,
pub ignore_unknown_instances: bool,
/// The path on the filesystem that the instance was read from the
/// filesystem if it came from the filesystem.
#[serde(serialize_with = "crate::path_serializer::serialize_option")]
pub source_path: Option<PathBuf>,
/// Information about the instance that came from the project that defined
/// it, if that's where it was defined.
///
/// A key-value pair where the key should be the name of the instance and
/// the value is the ProjectNode from the instance's project.
pub project_definition: Option<(String, ProjectNode)>,
}
pub struct RbxSession {
tree: RbxTree,
// TODO(#105): Change metadata_per_path to PathMap<Vec<MetadataPerPath>> for
// path aliasing.
metadata_per_path: PathMap<MetadataPerPath>,
instances_per_path: PathMap<HashSet<RbxId>>,
metadata_per_instance: HashMap<RbxId, MetadataPerInstance>,
message_queue: Arc<MessageQueue<InstanceChanges>>,
imfs: Arc<Mutex<Imfs>>,
@@ -52,17 +56,17 @@ impl RbxSession {
imfs: Arc<Mutex<Imfs>>,
message_queue: Arc<MessageQueue<InstanceChanges>>,
) -> RbxSession {
let mut metadata_per_path = PathMap::new();
let mut instances_per_path = PathMap::new();
let mut metadata_per_instance = HashMap::new();
let tree = {
let temp_imfs = imfs.lock().unwrap();
reify_initial_tree(&project, &temp_imfs, &mut metadata_per_path, &mut metadata_per_instance)
reify_initial_tree(&project, &temp_imfs, &mut instances_per_path, &mut metadata_per_instance)
};
RbxSession {
tree,
metadata_per_path,
instances_per_path,
metadata_per_instance,
message_queue,
imfs,
@@ -80,7 +84,7 @@ impl RbxSession {
.expect("Path was outside in-memory filesystem roots");
// Find the closest instance in the tree that currently exists
let mut path_to_snapshot = self.metadata_per_path.descend(root_path, path);
let mut path_to_snapshot = self.instances_per_path.descend(root_path, path);
// If this is a file that might affect its parent if modified, we
// should snapshot its parent instead.
@@ -93,42 +97,44 @@ impl RbxSession {
trace!("Snapshotting path {}", path_to_snapshot.display());
let path_metadata = self.metadata_per_path.get(&path_to_snapshot).unwrap();
let instances_at_path = self.instances_per_path.get(&path_to_snapshot)
.expect("Metadata did not exist for path")
.clone();
trace!("Metadata for path: {:?}", path_metadata);
for instance_id in &instances_at_path {
let instance_metadata = self.metadata_per_instance.get(&instance_id)
.expect("Metadata for instance ID did not exist");
let instance_id = path_metadata.instance_id
.expect("Instance did not exist in tree");
let maybe_snapshot = match &instance_metadata.project_definition {
Some((instance_name, project_node)) => {
snapshot_project_node(&imfs, &project_node, Cow::Owned(instance_name.clone()))
.unwrap_or_else(|_| panic!("Could not generate instance snapshot for path {}", path_to_snapshot.display()))
},
None => {
snapshot_imfs_path(&imfs, &path_to_snapshot, None)
.unwrap_or_else(|_| panic!("Could not generate instance snapshot for path {}", path_to_snapshot.display()))
},
};
// 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()));
let snapshot = match maybe_snapshot {
Some(snapshot) => snapshot,
None => {
trace!("Path resulted in no snapshot being generated.");
return;
},
};
let mut context = SnapshotContext {
metadata_per_path: &mut self.metadata_per_path,
};
let maybe_snapshot = snapshot_imfs_path(&imfs, &mut context, &path_to_snapshot, instance_name)
.unwrap_or_else(|_| panic!("Could not generate instance snapshot for path {}", path_to_snapshot.display()));
trace!("Snapshot: {:#?}", snapshot);
let snapshot = match maybe_snapshot {
Some(snapshot) => snapshot,
None => {
trace!("Path resulted in no snapshot being generated.");
return;
},
};
trace!("Snapshot: {:#?}", snapshot);
reconcile_subtree(
&mut self.tree,
instance_id,
&snapshot,
&mut self.metadata_per_path,
&mut self.metadata_per_instance,
&mut changes,
);
reconcile_subtree(
&mut self.tree,
*instance_id,
&snapshot,
&mut self.instances_per_path,
&mut self.metadata_per_instance,
&mut changes,
);
}
}
if changes.is_empty() {
@@ -170,13 +176,13 @@ impl RbxSession {
pub fn path_removed(&mut self, path: &Path) {
info!("Path removed: {}", path.display());
self.metadata_per_path.remove(path);
self.instances_per_path.remove(path);
self.path_created_or_updated(path);
}
pub fn path_renamed(&mut self, from_path: &Path, to_path: &Path) {
info!("Path renamed from {} to {}", from_path.display(), to_path.display());
self.metadata_per_path.remove(from_path);
self.instances_per_path.remove(from_path);
self.path_created_or_updated(from_path);
self.path_created_or_updated(to_path);
}
@@ -188,33 +194,26 @@ impl RbxSession {
pub fn get_instance_metadata(&self, id: RbxId) -> Option<&MetadataPerInstance> {
self.metadata_per_instance.get(&id)
}
pub fn debug_get_metadata_per_path(&self) -> &PathMap<MetadataPerPath> {
&self.metadata_per_path
}
}
pub fn construct_oneoff_tree(project: &Project, imfs: &Imfs) -> RbxTree {
let mut metadata_per_path = PathMap::new();
let mut instances_per_path = PathMap::new();
let mut metadata_per_instance = HashMap::new();
reify_initial_tree(project, imfs, &mut metadata_per_path, &mut metadata_per_instance)
reify_initial_tree(project, imfs, &mut instances_per_path, &mut metadata_per_instance)
}
fn reify_initial_tree(
project: &Project,
imfs: &Imfs,
metadata_per_path: &mut PathMap<MetadataPerPath>,
instances_per_path: &mut PathMap<HashSet<RbxId>>,
metadata_per_instance: &mut HashMap<RbxId, MetadataPerInstance>,
) -> RbxTree {
let mut context = SnapshotContext {
metadata_per_path,
};
let snapshot = snapshot_project_tree(imfs, &mut context, project)
let snapshot = snapshot_project_tree(imfs, project)
.expect("Could not snapshot project tree")
.expect("Project did not produce any instances");
let mut changes = InstanceChanges::default();
let tree = reify_root(&snapshot, metadata_per_path, metadata_per_instance, &mut changes);
let tree = reify_root(&snapshot, instances_per_path, metadata_per_instance, &mut changes);
tree
}