Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Three way merge #134

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions src/store/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,52 @@ impl StoreLayerBuilder {

Ok(())
}

// Apply changes required to change our parent layer into after merge.
// This is a three-way merge with other layers relative to the merge base if given.
pub fn apply_merge(
&self,
others: Vec<&StoreLayer>,
merge_base: Option<&StoreLayer>,
) -> Result<(), io::Error> {
rayon::join(
|| match merge_base {
Some(base) => {
base.triples().par_bridge().for_each(|b| {
if let Some(t) = base.id_triple_to_string(&b) {
if others
.iter()
.par_bridge()
.any(|o| !o.value_triple_exists(&t))
{
self.remove_value_triple(t).unwrap();
}
}
});
}
None => {}
},
|| {
others.iter().par_bridge().for_each(|os| {
os.triples().par_bridge().for_each(|o| {
if let Some(t) = os.id_triple_to_string(&o) {
match merge_base {
Some(base) => {
if !base.value_triple_exists(&t) {
self.add_value_triple(t).unwrap();
}
}
None => {
self.add_value_triple(t).unwrap();
}
}
}
})
})
},
);
Ok(())
}
}

/// A layer that keeps track of the store it came out of, allowing the creation of a layer builder on top of this layer.
Expand Down Expand Up @@ -1650,6 +1696,64 @@ mod tests {
.value_triple_exists(&ValueTriple::new_string_value("cat", "says", "meow")));
}

#[tokio::test]
async fn apply_a_merge() {
let store = open_memory_store();
let builder = store.create_base_layer().await.unwrap();

builder
.add_value_triple(ValueTriple::new_string_value("cow", "says", "moo"))
.unwrap();
builder
.add_value_triple(ValueTriple::new_string_value("cat", "says", "meow"))
.unwrap();

let merge_base = builder.commit().await.unwrap();

let builder2 = merge_base.open_write().await.unwrap();

builder2
.add_value_triple(ValueTriple::new_string_value("dog", "says", "woof"))
.unwrap();

let layer2 = builder2.commit().await.unwrap();

let builder3 = merge_base.open_write().await.unwrap();

builder3
.remove_value_triple(ValueTriple::new_string_value("cow", "says", "moo"))
.unwrap();

let layer3 = builder3.commit().await.unwrap();

let builder4 = merge_base.open_write().await.unwrap();

builder4
.add_value_triple(ValueTriple::new_string_value("bird", "says", "twe"))
.unwrap();

let layer4 = builder4.commit().await.unwrap();

let merge_builder = layer4.open_write().await.unwrap();

let _ = merge_builder.apply_merge(vec![&layer2, &layer3], Some(&merge_base));

let merged_layer = merge_builder.commit().await.unwrap();

assert!(
merged_layer.value_triple_exists(&ValueTriple::new_string_value("cat", "says", "meow"))
);
assert!(
merged_layer.value_triple_exists(&ValueTriple::new_string_value("bird", "says", "twe"))
);
assert!(
merged_layer.value_triple_exists(&ValueTriple::new_string_value("dog", "says", "woof"))
);
assert!(
!merged_layer.value_triple_exists(&ValueTriple::new_string_value("cow", "says", "moo"))
);
}

async fn cached_layer_name_does_not_change_after_rollup(store: Store) {
let builder = store.create_base_layer().await.unwrap();
let base_name = builder.name();
Expand Down
12 changes: 12 additions & 0 deletions src/store/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,18 @@ impl SyncStoreLayerBuilder {
pub fn apply_diff(&self, other: &SyncStoreLayer) -> Result<(), io::Error> {
self.inner.apply_diff(&other.inner)
}

/// Apply changes required to change our parent layer into after merge.
/// This is a three-way merge with other layers relative to the merge base if given.
pub fn apply_merge(
&self,
others: Vec<&SyncStoreLayer>,
merge_base: Option<&SyncStoreLayer>,
) -> Result<(), io::Error> {
let others_inner: Vec<&StoreLayer> = others.iter().map(|x| &x.inner).collect();
self.inner
.apply_merge(others_inner, merge_base.and_then(|x| Some(&x.inner)))
}
}

/// A layer that keeps track of the store it came out of, allowing the creation of a layer builder on top of this layer.
Expand Down