fix: changes traveler bug

should iter every change between two version, even when
a and b are parallel to each other
This commit is contained in:
Zixuan Chen 2023-08-05 19:47:15 +08:00
parent 7ab8ccff64
commit 3551bc4e99
7 changed files with 209 additions and 20 deletions

View file

@ -136,13 +136,6 @@ impl SharedArena {
}
pub fn set_parent(&self, child: ContainerIdx, parent: Option<ContainerIdx>) {
debug_log::debug_log!(
"set parent {:?} {:?} {:?} {:?}",
child,
parent,
self.get_container_id(child),
parent.map(|x| self.get_container_id(x))
);
self.inner.parents.lock().unwrap().insert(child, parent);
}

View file

@ -453,6 +453,15 @@ impl ListHandler {
.get_container_deep_value(self.container_idx)
}
pub fn get_deep_value_with_id(&self) -> LoroValue {
self.state
.upgrade()
.unwrap()
.lock()
.unwrap()
.get_container_deep_value_with_id(self.container_idx, None)
}
pub fn id(&self) -> ContainerID {
self.state
.upgrade()
@ -614,6 +623,15 @@ impl MapHandler {
.get_container_deep_value(self.container_idx)
}
pub fn get_deep_value_with_id(&self) -> LoroValue {
self.state
.upgrade()
.unwrap()
.lock()
.unwrap()
.get_container_deep_value_with_id(self.container_idx, None)
}
pub fn get(&self, key: &str) -> Option<LoroValue> {
self.state
.upgrade()

View file

@ -119,13 +119,14 @@ impl LoroDoc {
}
#[inline(always)]
pub fn with_txn<F>(&self, f: F) -> LoroResult<()>
pub fn with_txn<F, R>(&self, f: F) -> LoroResult<R>
where
F: Fn(&mut Transaction) -> LoroResult<()>,
F: FnOnce(&mut Transaction) -> LoroResult<R>,
{
let mut txn = self.txn().unwrap();
f(&mut txn)?;
txn.commit()
let v = f(&mut txn)?;
txn.commit()?;
Ok(v)
}
/// Create a new transaction with specified origin.
@ -346,6 +347,11 @@ impl LoroDoc {
self.state.lock().unwrap().get_deep_value()
}
/// Get deep value of the document with container id
pub fn get_deep_value_with_id(&self) -> LoroValue {
self.state.lock().unwrap().get_deep_value_with_id()
}
pub fn checkout_to_latest(&mut self) {
let f = self.oplog_frontiers();
self.checkout(&f);
@ -371,6 +377,7 @@ impl LoroDoc {
Some(frontiers),
);
debug_dbg!(&diff);
state.apply_diff(InternalDocDiff {
origin: "checkout".into(),
local: true,

View file

@ -432,20 +432,24 @@ impl OpLog {
decode_oplog(self, data)
}
/// Iterates over all changes between `from` and `to` peer by peer
/// Iterates over all changes between `a` and `b` peer by peer (not in causal order, fast)
pub(crate) fn for_each_change_within(
&self,
from: &VersionVector,
to: &VersionVector,
a: &VersionVector,
b: &VersionVector,
mut f: impl FnMut(&Change),
) {
for (peer, changes) in self.changes.iter() {
let from_cnt = from.get(peer).copied().unwrap_or(0);
let to_cnt = to.get(peer).copied().unwrap_or(0);
let mut from_cnt = a.get(peer).copied().unwrap_or(0);
let mut to_cnt = b.get(peer).copied().unwrap_or(0);
if from_cnt == to_cnt {
continue;
}
if to_cnt < from_cnt {
std::mem::swap(&mut from_cnt, &mut to_cnt);
}
let Some(result) = changes.get_by_atom_index(from_cnt) else { continue };
for i in result.merged_index..changes.vec().len() {
f(&changes.vec()[i])

View file

@ -11,6 +11,7 @@ use crate::{
delta::{Delta, DeltaItem},
event::InternalContainerDiff,
event::{Diff, Index},
fx_map,
id::PeerID,
op::RawOp,
version::Frontiers,
@ -412,6 +413,93 @@ impl DocState {
LoroValue::Map(Arc::new(ans))
}
pub fn get_deep_value_with_id(&self) -> LoroValue {
let roots = self.arena.root_containers();
let mut ans = FxHashMap::with_capacity_and_hasher(roots.len(), Default::default());
for root_idx in roots {
let id = self.arena.idx_to_id(root_idx).unwrap();
match id.clone() {
loro_common::ContainerID::Root { name, .. } => {
ans.insert(
name.to_string(),
self.get_container_deep_value_with_id(root_idx, Some(id)),
);
}
loro_common::ContainerID::Normal { .. } => {
unreachable!()
}
}
}
LoroValue::Map(Arc::new(ans))
}
pub(crate) fn get_container_deep_value_with_id(
&self,
container: ContainerIdx,
id: Option<ContainerID>,
) -> LoroValue {
let id = id.unwrap_or_else(|| self.arena.idx_to_id(container).unwrap());
let Some(state) = self.states.get(&container) else {
return container.get_type().default_value();
};
let value = state.get_value();
let cid_str =
LoroValue::String(Arc::new(format!("idx:{}, id:{}", container.to_index(), id)));
match value {
LoroValue::Container(_) => unreachable!(),
LoroValue::List(mut list) => {
if list.iter().all(|x| !x.is_container()) {
return LoroValue::Map(Arc::new(fx_map!(
"cid".into() => cid_str,
"value".into() => LoroValue::List(list)
)));
}
let list_mut = Arc::make_mut(&mut list);
for item in list_mut.iter_mut() {
if item.is_container() {
let container = item.as_container().unwrap();
let container_idx = self.arena.register_container(container);
let value = self.get_container_deep_value_with_id(
container_idx,
Some(container.clone()),
);
*item = value;
}
}
LoroValue::Map(Arc::new(fx_map!(
"cid".into() => cid_str,
"value".into() => LoroValue::List(list)
)))
}
LoroValue::Map(mut map) => {
let map_mut = Arc::make_mut(&mut map);
for (_key, value) in map_mut.iter_mut() {
if value.is_container() {
let container = value.as_container().unwrap();
let container_idx = self.arena.register_container(container);
let new_value = self.get_container_deep_value_with_id(
container_idx,
Some(container.clone()),
);
*value = new_value;
}
}
LoroValue::Map(Arc::new(fx_map!(
"cid".into() => cid_str,
"value".into() => LoroValue::Map(map)
)))
}
_ => LoroValue::Map(Arc::new(fx_map!(
"cid".into() => cid_str,
"value".into() => value
))),
}
}
pub fn get_container_deep_value(&self, container: ContainerIdx) -> LoroValue {
let Some(state) = self.states.get(&container) else {
return container.get_type().default_value();

View file

@ -260,7 +260,6 @@ impl ListState {
impl ContainerState for ListState {
fn apply_diff(&mut self, diff: &mut Diff, arena: &SharedArena) {
debug_log::debug_log!("Apply List Diff {:#?}", diff);
match diff {
Diff::List(delta) => {
let mut index = 0;
@ -300,7 +299,6 @@ impl ContainerState for ListState {
for slices in value.0.iter() {
for i in slices.0.start..slices.0.end {
let value = arena.get_value(i as usize).unwrap();
debug_dbg!(&value);
if value.is_container() {
let c = value.as_container().unwrap();
let idx = arena.register_container(c);
@ -321,8 +319,6 @@ impl ContainerState for ListState {
}
_ => unreachable!(),
};
debug_dbg!(&self.idx);
debug_dbg!(&self.get_value());
}
fn apply_op(&mut self, op: RawOp, arena: &SharedArena) {

View file

@ -100,6 +100,89 @@ fn map_checkout() {
assert_eq!(meta.get_deep_value().to_json(), r#"{"key":1}"#);
}
#[test]
fn a_list_of_map_checkout() {
let mut doc = LoroDoc::new();
let entry = doc.get_map("entry");
let (list, sub) = doc
.with_txn(|txn| {
let list = entry
.insert_container(txn, "list", loro_common::ContainerType::List)?
.into_list()
.unwrap();
let sub_map = list
.insert_container(txn, 0, loro_common::ContainerType::Map)?
.into_map()
.unwrap();
sub_map.insert(txn, "x", 100.into())?;
sub_map.insert(txn, "y", 1000.into())?;
Ok((list, sub_map))
})
.unwrap();
let v0 = doc.oplog_frontiers();
let d0 = doc.get_deep_value().to_json();
doc.with_txn(|txn| {
list.insert(txn, 0, 3.into())?;
list.push(txn, 4.into())?;
list.insert_container(txn, 2, loro_common::ContainerType::Map)?;
list.insert_container(txn, 3, loro_common::ContainerType::Map)?;
Ok(())
})
.unwrap();
doc.with_txn(|txn| {
list.delete(txn, 2, 1)?;
Ok(())
})
.unwrap();
doc.with_txn(|txn| {
sub.insert(txn, "x", 9.into())?;
sub.insert(txn, "y", 9.into())?;
Ok(())
})
.unwrap();
doc.with_txn(|txn| {
sub.insert(txn, "z", 9.into())?;
Ok(())
})
.unwrap();
let v1 = doc.oplog_frontiers();
let d1 = doc.get_deep_value().to_json();
doc.with_txn(|txn| {
sub.insert(txn, "x", 77.into())?;
Ok(())
})
.unwrap();
doc.with_txn(|txn| {
sub.insert(txn, "y", 88.into())?;
Ok(())
})
.unwrap();
doc.with_txn(|txn| {
list.delete(txn, 0, 1)?;
list.insert(txn, 0, 123.into())?;
list.push(txn, 99.into())?;
Ok(())
})
.unwrap();
let v2 = doc.oplog_frontiers();
let d2 = doc.get_deep_value().to_json();
doc.checkout(&v0);
assert_eq!(doc.get_deep_value().to_json(), d0);
doc.checkout(&v1);
assert_eq!(doc.get_deep_value().to_json(), d1);
doc.checkout(&v2);
println!("{}", doc.get_deep_value_with_id().to_json_pretty());
assert_eq!(doc.get_deep_value().to_json(), d2);
debug_log::group!("checking out v1");
doc.checkout(&v1);
debug_log::group_end!();
println!("{}", doc.get_deep_value_with_id().to_json_pretty());
assert_eq!(doc.get_deep_value().to_json(), d1);
doc.checkout(&v0);
assert_eq!(doc.get_deep_value().to_json(), d0);
}
#[test]
fn map_concurrent_checkout() {
let mut doc_a = LoroDoc::new();