refactor: reduce tracker mem usage by using nonmax id (#282)

This commit is contained in:
Zixuan Chen 2024-02-29 22:55:57 +08:00 committed by GitHub
parent cadde3cb55
commit 06e3a5420d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 72 additions and 22 deletions

7
Cargo.lock generated
View file

@ -778,6 +778,7 @@ dependencies = [
"fxhash",
"js-sys",
"loro-rle",
"nonmax",
"serde",
"serde_columnar",
"string_cache",
@ -939,6 +940,12 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
[[package]]
name = "nonmax"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51"
[[package]]
name = "num"
version = "0.4.1"

View file

@ -23,6 +23,7 @@ string_cache = "0.8"
arbitrary = { version = "1.3.0", features = ["derive"] }
js-sys = { version = "0.3.60", optional = true }
serde_columnar = "0.3.3"
nonmax = "0.5.5"
[features]
wasm = ["wasm-bindgen", "js-sys"]

View file

@ -3,6 +3,7 @@ use std::{fmt::Display, sync::Arc};
use arbitrary::Arbitrary;
use enum_as_inner::EnumAsInner;
use nonmax::NonMaxI32;
use serde::{Deserialize, Serialize};
mod error;
mod id;
@ -32,6 +33,41 @@ pub struct ID {
pub counter: Counter,
}
/// It's the unique ID of an Op represented by [PeerID] and [Counter].
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub struct CompactId {
pub peer: PeerID,
pub counter: NonMaxI32,
}
impl CompactId {
pub fn new(peer: PeerID, counter: Counter) -> Self {
Self {
peer,
counter: NonMaxI32::new(counter).unwrap(),
}
}
pub fn to_id(&self) -> ID {
ID {
peer: self.peer,
counter: self.counter.get(),
}
}
}
impl TryFrom<ID> for CompactId {
type Error = ID;
fn try_from(id: ID) -> Result<Self, ID> {
if id.counter == i32::MAX {
return Err(id);
}
Ok(Self::new(id.peer, id.counter))
}
}
/// It's the unique ID of an Op represented by [PeerID] and [Lamport] clock.
/// It's used to define the total order of Ops.
#[derive(PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize, PartialOrd, Ord)]

View file

@ -1,7 +1,7 @@
use std::ops::Range;
use generic_btree::rle::{HasLength, Mergeable, Sliceable};
use loro_common::{Counter, HasId, IdFull, IdSpan, Lamport, ID};
use loro_common::{CompactId, Counter, HasId, IdFull, IdSpan, Lamport, ID};
use serde::{Deserialize, Serialize};
use super::AnchorType;
@ -176,8 +176,8 @@ pub(super) struct FugueSpan {
/// The status at the `new` version.
/// It's used when calculating diff.
pub diff_status: Option<Status>,
pub origin_left: Option<ID>,
pub origin_right: Option<ID>,
pub origin_left: Option<CompactId>,
pub origin_right: Option<CompactId>,
pub content: RichtextChunk,
}
@ -223,7 +223,13 @@ impl Sliceable for FugueSpan {
origin_left: if range.start == 0 {
self.origin_left
} else {
Some(self.id.inc((range.start - 1) as Counter).id())
Some(
self.id
.inc((range.start - 1) as Counter)
.id()
.try_into()
.unwrap(),
)
},
origin_right: self.origin_right,
content: self.content._slice(range),
@ -247,7 +253,7 @@ impl Mergeable for FugueSpan {
&& self.id.lamport + self.content.len() as Lamport == rhs.id.lamport
&& rhs.origin_left.is_some()
&& rhs.origin_left.unwrap().peer == self.id.peer
&& rhs.origin_left.unwrap().counter
&& rhs.origin_left.unwrap().counter.get()
== self.id.counter + self.content.len() as Counter - 1
&& self.origin_right == rhs.origin_right
&& self.content.can_merge(&rhs.content)

View file

@ -109,7 +109,7 @@ impl CrdtRope {
}
_ => {
// Otherwise, we need to test whether origin_right's origin_left == this.origin_left
if iter.elem.origin_left == origin_left {
if iter.elem.origin_left.map(|x| x.to_id()) == origin_left {
Some(origin_right)
} else {
None
@ -127,8 +127,8 @@ impl CrdtRope {
(origin_right, parent_right_idx, in_between)
};
content.origin_left = origin_left;
content.origin_right = origin_right;
content.origin_left = origin_left.map(|x| x.try_into().unwrap());
content.origin_right = origin_right.map(|x| x.try_into().unwrap());
(parent_right_leaf, in_between)
};
@ -144,7 +144,7 @@ impl CrdtRope {
let other_origin_left = other_elem.origin_left;
if other_origin_left != content.origin_left
&& other_origin_left
.map(|left| visited.iter().all(|x| !x.contains_id(left)))
.map(|left| visited.iter().all(|x| !x.contains_id(left.to_id())))
.unwrap_or(true)
{
// The other_elem's origin_left must be at the left side of content's origin_left.
@ -176,10 +176,10 @@ impl CrdtRope {
let other_parent_right_idx =
if let Some(other_origin_right) = other_elem.origin_right {
let elem_idx = find_elem(other_origin_right);
let elem_idx = find_elem(other_origin_right.to_id());
let elem = self.tree.get_elem(elem_idx).unwrap();
// It must be the start of the elem
assert_eq!(elem.id.id(), other_origin_right);
assert_eq!(elem.id.id(), other_origin_right.to_id());
if elem.origin_left == content.origin_left {
Some(elem_idx)
} else {
@ -621,7 +621,7 @@ impl LeafUpdate {
mod test {
use std::ops::Range;
use loro_common::{Counter, PeerID, ID};
use loro_common::{CompactId, Counter, PeerID, ID};
use crate::container::richtext::RichtextChunk;
@ -726,8 +726,8 @@ mod test {
let mut rope = CrdtRope::new();
rope.insert(0, span(0, 0..10), |_| panic!());
let fugue = rope.insert(5, span(1, 10..20), |_| panic!()).content;
assert_eq!(fugue.origin_left, Some(ID::new(0, 4)));
assert_eq!(fugue.origin_right, Some(ID::new(0, 5)));
assert_eq!(fugue.origin_left, Some(CompactId::new(0, 4)));
assert_eq!(fugue.origin_right, Some(CompactId::new(0, 5)));
}
#[test]
@ -738,11 +738,11 @@ mod test {
rope.delete(5, 2, |_| {});
assert_eq!(rope.len(), 8);
let fugue = rope.insert(6, span(1, 10..20), |_| panic!()).content;
assert_eq!(fugue.origin_left, Some(ID::new(0, 7)));
assert_eq!(fugue.origin_right, Some(ID::new(0, 8)));
assert_eq!(fugue.origin_left, Some(CompactId::new(0, 7)));
assert_eq!(fugue.origin_right, Some(CompactId::new(0, 8)));
let fugue = rope.insert(5, span(1, 10..11), |_| panic!()).content;
assert_eq!(fugue.origin_left, Some(ID::new(0, 4)));
assert_eq!(fugue.origin_right, Some(ID::new(0, 5)));
assert_eq!(fugue.origin_left, Some(CompactId::new(0, 4)));
assert_eq!(fugue.origin_right, Some(CompactId::new(0, 5)));
}
#[test]
@ -753,8 +753,8 @@ mod test {
rope.insert(0, span(0, 0..10), |_| panic!());
rope.insert(5, future_span(1, 10..20), |_| panic!());
let fugue = rope.insert(5, span(1, 10..20), |_| panic!()).content;
assert_eq!(fugue.origin_left, Some(ID::new(0, 4)));
assert_eq!(fugue.origin_right, Some(ID::new(0, 5)));
assert_eq!(fugue.origin_left, Some(CompactId::new(0, 4)));
assert_eq!(fugue.origin_right, Some(CompactId::new(0, 5)));
}
{
// insert deleted
@ -762,8 +762,8 @@ mod test {
rope.insert(0, span(0, 0..10), |_| panic!());
rope.insert(5, dead_span(1, 10..20), |_| panic!());
let fugue = rope.insert(5, span(1, 10..20), |_| panic!()).content;
assert_eq!(fugue.origin_left, Some(ID::new(0, 4)));
assert_eq!(fugue.origin_right, Some(ID::new(1, 0)));
assert_eq!(fugue.origin_left, Some(CompactId::new(0, 4)));
assert_eq!(fugue.origin_right, Some(CompactId::new(1, 0)));
}
}