WIP
This commit is contained in:
parent
93701f9c5d
commit
05ec6b89c2
3 changed files with 496 additions and 391 deletions
|
@ -60,6 +60,16 @@ where
|
||||||
Ok(Self(Sequence::load_root(id, kv).await?))
|
Ok(Self(Sequence::load_root(id, kv).await?))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn load_all(id: SavedId, kv: &dyn KvStore) -> Result<Self>
|
||||||
|
where
|
||||||
|
K: Serialize + for<'de> Deserialize<'de>,
|
||||||
|
V: Serialize + for<'de> Deserialize<'de>,
|
||||||
|
{
|
||||||
|
let mut sequence = Sequence::load_root(id, kv).await?;
|
||||||
|
sequence.load(kv, &(), |_| true).await?;
|
||||||
|
Ok(Self(sequence))
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn load(&mut self, key: &K, kv: &dyn KvStore) -> Result<Option<&V>>
|
pub async fn load(&mut self, key: &K, kv: &dyn KvStore) -> Result<Option<&V>>
|
||||||
where
|
where
|
||||||
K: Serialize + for<'de> Deserialize<'de>,
|
K: Serialize + for<'de> Deserialize<'de>,
|
||||||
|
@ -77,6 +87,21 @@ where
|
||||||
Ok(self.get(key))
|
Ok(self.get(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn load_from(
|
||||||
|
&mut self,
|
||||||
|
start: &K,
|
||||||
|
kv: &dyn KvStore,
|
||||||
|
) -> Result<impl Iterator<Item = (&K, &V)>>
|
||||||
|
where
|
||||||
|
K: Serialize + for<'de> Deserialize<'de>,
|
||||||
|
V: Serialize + for<'de> Deserialize<'de>,
|
||||||
|
{
|
||||||
|
self.0
|
||||||
|
.load(kv, &(), |probe| probe.start.0 >= *start)
|
||||||
|
.await?;
|
||||||
|
Ok(self.iter_from(start))
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn store(&mut self, key: K, value: V, kv: &dyn KvStore) -> Result<()>
|
pub async fn store(&mut self, key: K, value: V, kv: &dyn KvStore) -> Result<()>
|
||||||
where
|
where
|
||||||
K: Serialize + for<'de> Deserialize<'de>,
|
K: Serialize + for<'de> Deserialize<'de>,
|
||||||
|
@ -164,33 +189,7 @@ where
|
||||||
cursor.item().map(|item| (&item.key, &item.value))
|
cursor.item().map(|item| (&item.key, &item.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn range<'a, R>(&self, range: R) -> impl Iterator<Item = (&K, &V)>
|
pub fn iter_from<'a>(&self, from: &'a K) -> impl Iterator<Item = (&K, &V)> {
|
||||||
where
|
|
||||||
K: 'a,
|
|
||||||
R: RangeBounds<&'a K>,
|
|
||||||
{
|
|
||||||
let mut cursor = self.0.cursor::<MapKeyRef<'_, K>>();
|
|
||||||
match range.start_bound() {
|
|
||||||
Bound::Included(start) => {
|
|
||||||
let start = MapKeyRef(Some(*start));
|
|
||||||
cursor.seek(&start, Bias::Left, &());
|
|
||||||
}
|
|
||||||
Bound::Excluded(start) => {
|
|
||||||
let start = MapKeyRef(Some(*start));
|
|
||||||
cursor.seek(&start, Bias::Right, &());
|
|
||||||
}
|
|
||||||
Bound::Unbounded => cursor.next(&()),
|
|
||||||
}
|
|
||||||
cursor
|
|
||||||
.map(|entry| (&entry.key, &entry.value))
|
|
||||||
.take_while(move |(key, _)| match range.end_bound() {
|
|
||||||
Bound::Included(end) => key <= end,
|
|
||||||
Bound::Excluded(end) => key < end,
|
|
||||||
Bound::Unbounded => true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter_from<'a>(&'a self, from: &'a K) -> impl Iterator<Item = (&K, &V)> + '_ {
|
|
||||||
let mut cursor = self.0.cursor::<MapKeyRef<'_, K>>();
|
let mut cursor = self.0.cursor::<MapKeyRef<'_, K>>();
|
||||||
let from_key = MapKeyRef(Some(from));
|
let from_key = MapKeyRef(Some(from));
|
||||||
cursor.seek(&from_key, Bias::Left, &());
|
cursor.seek(&from_key, Bias::Left, &());
|
||||||
|
|
|
@ -14,6 +14,7 @@ use btree::{Bias, KvStore, SavedId};
|
||||||
use collections::{btree_map, BTreeMap, BTreeSet, Bound, HashMap, HashSet, VecDeque};
|
use collections::{btree_map, BTreeMap, BTreeSet, Bound, HashMap, HashSet, VecDeque};
|
||||||
use dense_id::DenseId;
|
use dense_id::DenseId;
|
||||||
use futures::{channel::mpsc, future::BoxFuture, FutureExt, StreamExt};
|
use futures::{channel::mpsc, future::BoxFuture, FutureExt, StreamExt};
|
||||||
|
use history::{History, SavedHistory};
|
||||||
use messages::{MessageEnvelope, Operation, RequestEnvelope};
|
use messages::{MessageEnvelope, Operation, RequestEnvelope};
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use rope::Rope;
|
use rope::Rope;
|
||||||
|
@ -138,7 +139,7 @@ impl OperationId {
|
||||||
pub fn new(replica_id: ReplicaId) -> Self {
|
pub fn new(replica_id: ReplicaId) -> Self {
|
||||||
Self {
|
Self {
|
||||||
replica_id,
|
replica_id,
|
||||||
operation_count: OperationCount::default(),
|
operation_count: OperationCount(1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,25 +382,28 @@ impl<E: Executor, N: ClientNetwork> Checkout<E, N> {
|
||||||
let response = client
|
let response = client
|
||||||
.request(messages::SyncRepo {
|
.request(messages::SyncRepo {
|
||||||
id: self.repo.id,
|
id: self.repo.id,
|
||||||
max_operation_ids: self.repo.read(|repo| (&repo.max_operation_ids).into()),
|
max_operation_ids: self
|
||||||
})
|
.repo
|
||||||
.await?;
|
.read(|repo| repo.history.max_operation_ids().into()),
|
||||||
|
|
||||||
self.repo
|
|
||||||
.update_async(|repo| {
|
|
||||||
let kv = client.db.kv.clone();
|
|
||||||
let operations = response.operations.clone();
|
|
||||||
async move {
|
|
||||||
repo.apply_operations(operations, &*kv).await?;
|
|
||||||
Ok((None, ()))
|
|
||||||
}
|
|
||||||
.boxed()
|
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let operations = self
|
let operations = self
|
||||||
.repo
|
.repo
|
||||||
.read(|snapshot| snapshot.operations_since(&(&response.max_operation_ids).into()));
|
.update_async(|repo| {
|
||||||
|
let kv = client.db.kv.clone();
|
||||||
|
let response = response.clone();
|
||||||
|
async move {
|
||||||
|
repo.apply_operations(response.operations, &*kv).await?;
|
||||||
|
let operations = repo
|
||||||
|
.history
|
||||||
|
.operations_since(&(&response.max_operation_ids).into(), &*kv)
|
||||||
|
.await?;
|
||||||
|
Ok((None, operations))
|
||||||
|
}
|
||||||
|
.boxed()
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
for chunk in operations.chunks(CHUNK_SIZE) {
|
for chunk in operations.chunks(CHUNK_SIZE) {
|
||||||
client
|
client
|
||||||
|
@ -737,12 +741,25 @@ impl<N: ServerNetwork> Server<N> {
|
||||||
.repo(request.id)
|
.repo(request.id)
|
||||||
.ok_or_else(|| anyhow!("repo not found"))?;
|
.ok_or_else(|| anyhow!("repo not found"))?;
|
||||||
|
|
||||||
repo.read(|snapshot| {
|
repo.update_async(|snapshot| {
|
||||||
Ok(messages::SyncRepoResponse {
|
let request = request.clone();
|
||||||
operations: snapshot.operations_since(&(&request.max_operation_ids).into()),
|
let kv = self.db.kv.clone();
|
||||||
max_operation_ids: (&snapshot.max_operation_ids).into(),
|
async move {
|
||||||
})
|
let operations = snapshot
|
||||||
|
.history
|
||||||
|
.operations_since(&(&request.max_operation_ids).into(), &*kv)
|
||||||
|
.await?;
|
||||||
|
Ok((
|
||||||
|
None,
|
||||||
|
messages::SyncRepoResponse {
|
||||||
|
operations,
|
||||||
|
max_operation_ids: snapshot.history.max_operation_ids().into(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
.boxed()
|
||||||
})
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_publish_operations(
|
async fn handle_publish_operations(
|
||||||
|
@ -899,7 +916,7 @@ impl Repo {
|
||||||
.update(&self.id, |repo| {
|
.update(&self.id, |repo| {
|
||||||
let (operation, result) = f(repo);
|
let (operation, result) = f(repo);
|
||||||
if let Some(operation) = operation {
|
if let Some(operation) = operation {
|
||||||
repo.save_local_operation(operation.clone());
|
repo.history.insert_local(operation.clone());
|
||||||
if let Some(local_operation_created) = self.db.local_operation_created.as_ref()
|
if let Some(local_operation_created) = self.db.local_operation_created.as_ref()
|
||||||
{
|
{
|
||||||
local_operation_created(self.id, operation);
|
local_operation_created(self.id, operation);
|
||||||
|
@ -1007,7 +1024,7 @@ impl Branch {
|
||||||
let mut revision = repo
|
let mut revision = repo
|
||||||
.cached_revision(&head)
|
.cached_revision(&head)
|
||||||
.expect("head revision must exist");
|
.expect("head revision must exist");
|
||||||
let operation_id = repo.last_operation_id.tick();
|
let operation_id = repo.history.next_operation_id();
|
||||||
let (operation, result) = f(operation_id, head.clone(), &mut revision);
|
let (operation, result) = f(operation_id, head.clone(), &mut revision);
|
||||||
repo.branches
|
repo.branches
|
||||||
.update(&self.id, |branch| branch.head = operation_id.into());
|
.update(&self.id, |branch| branch.head = operation_id.into());
|
||||||
|
@ -1686,47 +1703,36 @@ impl<'a> RopeBuilder<'a> {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct RepoSnapshot {
|
pub struct RepoSnapshot {
|
||||||
id: RepoId,
|
id: RepoId,
|
||||||
last_operation_id: OperationId,
|
history: History,
|
||||||
branches: btree::Map<OperationId, BranchSnapshot>,
|
branches: btree::Map<OperationId, BranchSnapshot>,
|
||||||
// TODO: Change String to Arc<str> for branch_ids_by_name
|
// TODO: Change String to Arc<str> for branch_ids_by_name
|
||||||
branch_ids_by_name: btree::Map<String, OperationId>,
|
branch_ids_by_name: btree::Map<String, OperationId>,
|
||||||
operations: btree::Map<OperationId, Operation>,
|
|
||||||
revisions: Arc<Mutex<HashMap<RevisionId, Revision>>>,
|
revisions: Arc<Mutex<HashMap<RevisionId, Revision>>>,
|
||||||
max_operation_ids: btree::Map<ReplicaId, OperationCount>,
|
|
||||||
deferred_operations: btree::Sequence<DeferredOperation>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct SavedRepoSnapshot {
|
struct SavedRepoSnapshot {
|
||||||
last_operation_id: OperationId,
|
history: SavedHistory,
|
||||||
branches: btree::SavedId,
|
branches: btree::SavedId,
|
||||||
branch_ids_by_name: btree::SavedId,
|
branch_ids_by_name: btree::SavedId,
|
||||||
operations: btree::SavedId,
|
|
||||||
max_operation_ids: btree::SavedId,
|
|
||||||
deferred_operations: btree::SavedId,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RepoSnapshot {
|
impl RepoSnapshot {
|
||||||
fn new(id: RepoId, replica_id: ReplicaId) -> Self {
|
fn new(id: RepoId, replica_id: ReplicaId) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
last_operation_id: OperationId::new(replica_id),
|
history: History::new(replica_id),
|
||||||
branches: Default::default(),
|
branches: Default::default(),
|
||||||
branch_ids_by_name: Default::default(),
|
branch_ids_by_name: Default::default(),
|
||||||
operations: Default::default(),
|
|
||||||
revisions: Default::default(),
|
revisions: Default::default(),
|
||||||
max_operation_ids: Default::default(),
|
|
||||||
deferred_operations: Default::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ptr_eq(this: &Self, other: &Self) -> bool {
|
fn ptr_eq(this: &Self, other: &Self) -> bool {
|
||||||
btree::Map::ptr_eq(&this.branches, &other.branches)
|
this.id == other.id
|
||||||
&& btree::Map::ptr_eq(&this.branches, &other.branches)
|
&& btree::Map::ptr_eq(&this.branches, &other.branches)
|
||||||
&& btree::Map::ptr_eq(&this.branch_ids_by_name, &other.branch_ids_by_name)
|
&& btree::Map::ptr_eq(&this.branch_ids_by_name, &other.branch_ids_by_name)
|
||||||
&& btree::Map::ptr_eq(&this.operations, &other.operations)
|
&& History::ptr_eq(&this.history, &other.history)
|
||||||
&& btree::Map::ptr_eq(&this.max_operation_ids, &other.max_operation_ids)
|
|
||||||
&& this.last_operation_id == other.last_operation_id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn load(id: RepoId, kv: &dyn KvStore) -> Result<Self> {
|
async fn load(id: RepoId, kv: &dyn KvStore) -> Result<Self> {
|
||||||
|
@ -1734,25 +1740,18 @@ impl RepoSnapshot {
|
||||||
let saved_repo = serde_bare::from_slice::<SavedRepoSnapshot>(&repo_bytes)?;
|
let saved_repo = serde_bare::from_slice::<SavedRepoSnapshot>(&repo_bytes)?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
id,
|
id,
|
||||||
last_operation_id: saved_repo.last_operation_id,
|
history: History::load(saved_repo.history, kv).await?,
|
||||||
branches: btree::Map::load_root(saved_repo.branches, kv).await?,
|
branches: btree::Map::load_root(saved_repo.branches, kv).await?,
|
||||||
branch_ids_by_name: btree::Map::load_root(saved_repo.branch_ids_by_name, kv).await?,
|
branch_ids_by_name: btree::Map::load_root(saved_repo.branch_ids_by_name, kv).await?,
|
||||||
operations: btree::Map::load_root(saved_repo.operations, kv).await?,
|
|
||||||
revisions: Default::default(),
|
revisions: Default::default(),
|
||||||
max_operation_ids: btree::Map::load_root(saved_repo.max_operation_ids, kv).await?,
|
|
||||||
deferred_operations: btree::Sequence::load_root(saved_repo.deferred_operations, kv)
|
|
||||||
.await?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn save(&self, id: RepoId, kv: &dyn KvStore) -> Result<()> {
|
async fn save(&self, id: RepoId, kv: &dyn KvStore) -> Result<()> {
|
||||||
let saved_repo = SavedRepoSnapshot {
|
let saved_repo = SavedRepoSnapshot {
|
||||||
last_operation_id: self.last_operation_id,
|
history: self.history.save(kv).await?,
|
||||||
branches: self.branches.save(kv).await?,
|
branches: self.branches.save(kv).await?,
|
||||||
branch_ids_by_name: self.branch_ids_by_name.save(kv).await?,
|
branch_ids_by_name: self.branch_ids_by_name.save(kv).await?,
|
||||||
operations: self.operations.save(kv).await?,
|
|
||||||
max_operation_ids: self.max_operation_ids.save(kv).await?,
|
|
||||||
deferred_operations: self.deferred_operations.save(kv).await?,
|
|
||||||
};
|
};
|
||||||
let repo_bytes = serde_bare::to_vec(&saved_repo)?;
|
let repo_bytes = serde_bare::to_vec(&saved_repo)?;
|
||||||
kv.store(id.to_be_bytes(), "root".into(), repo_bytes)
|
kv.store(id.to_be_bytes(), "root".into(), repo_bytes)
|
||||||
|
@ -1762,7 +1761,7 @@ impl RepoSnapshot {
|
||||||
|
|
||||||
fn create_empty_branch(&mut self, name: impl Into<Arc<str>>) -> (Operation, OperationId) {
|
fn create_empty_branch(&mut self, name: impl Into<Arc<str>>) -> (Operation, OperationId) {
|
||||||
let name = name.into();
|
let name = name.into();
|
||||||
let branch_id = self.last_operation_id.tick();
|
let branch_id = self.history.next_operation_id();
|
||||||
self.branches.insert(
|
self.branches.insert(
|
||||||
branch_id,
|
branch_id,
|
||||||
BranchSnapshot {
|
BranchSnapshot {
|
||||||
|
@ -1781,42 +1780,6 @@ impl RepoSnapshot {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operations_since(&self, version: &btree::Map<ReplicaId, OperationCount>) -> Vec<Operation> {
|
|
||||||
let mut new_operations = Vec::new();
|
|
||||||
for (replica_id, end_op_count) in self.max_operation_ids.iter() {
|
|
||||||
let end_op = OperationId {
|
|
||||||
replica_id: *replica_id,
|
|
||||||
operation_count: *end_op_count,
|
|
||||||
};
|
|
||||||
if let Some(start_op_count) = version.get(&replica_id) {
|
|
||||||
let start_op = OperationId {
|
|
||||||
replica_id: *replica_id,
|
|
||||||
operation_count: *start_op_count,
|
|
||||||
};
|
|
||||||
new_operations.extend(
|
|
||||||
self.operations
|
|
||||||
.range((Bound::Excluded(&start_op), Bound::Included(&end_op)))
|
|
||||||
.map(|(_, op)| op.clone()),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
let start_op = OperationId::new(*replica_id);
|
|
||||||
new_operations.extend(
|
|
||||||
self.operations
|
|
||||||
.range((Bound::Included(&start_op), Bound::Included(&end_op)))
|
|
||||||
.map(|(_, op)| op.clone()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
new_operations
|
|
||||||
}
|
|
||||||
|
|
||||||
fn save_local_operation(&mut self, operation: Operation) {
|
|
||||||
let replica_id = operation.id().replica_id;
|
|
||||||
let count = operation.id().operation_count;
|
|
||||||
self.max_operation_ids.insert(replica_id, count);
|
|
||||||
self.operations.insert(operation.id(), operation);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Apply the given operations and any deferred operations that are now applicable.
|
/// Apply the given operations and any deferred operations that are now applicable.
|
||||||
async fn apply_operations(
|
async fn apply_operations(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -1836,15 +1799,11 @@ impl RepoSnapshot {
|
||||||
operation: Operation,
|
operation: Operation,
|
||||||
kv: &dyn KvStore,
|
kv: &dyn KvStore,
|
||||||
) -> Result<SmallVec<[Operation; 1]>> {
|
) -> Result<SmallVec<[Operation; 1]>> {
|
||||||
if self.operations.contains_key(&operation.id()) {
|
if self.history.has_applied(&operation, kv).await? {
|
||||||
return Ok(Default::default());
|
return Ok(Default::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
if operation
|
if self.history.can_apply(&operation, kv).await? {
|
||||||
.parent()
|
|
||||||
.iter()
|
|
||||||
.all(|parent| self.operations.contains_key(&parent))
|
|
||||||
{
|
|
||||||
let operation_id = operation.id();
|
let operation_id = operation.id();
|
||||||
let mut new_head;
|
let mut new_head;
|
||||||
match &operation {
|
match &operation {
|
||||||
|
@ -1864,7 +1823,7 @@ impl RepoSnapshot {
|
||||||
| Operation::Edit(operations::Edit {
|
| Operation::Edit(operations::Edit {
|
||||||
branch_id, parent, ..
|
branch_id, parent, ..
|
||||||
}) => {
|
}) => {
|
||||||
if let Some(branch) = self.branches.get(branch_id).cloned() {
|
if let Some(branch) = self.branches.load(branch_id, kv).await?.cloned() {
|
||||||
new_head = branch.head;
|
new_head = branch.head;
|
||||||
new_head.observe(operation_id, &parent);
|
new_head.observe(operation_id, &parent);
|
||||||
self.branches
|
self.branches
|
||||||
|
@ -1880,8 +1839,7 @@ impl RepoSnapshot {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.save_remote_operation(operation, kv).await?;
|
let flushed_operations = self.history.insert(operation, kv).await?;
|
||||||
let flushed_operations = self.flush_deferred_operations(operation_id, kv).await?;
|
|
||||||
|
|
||||||
// The following ensures that a revision for the branch head is always present.
|
// The following ensures that a revision for the branch head is always present.
|
||||||
#[cfg(not(any(test, feature = "test-support")))]
|
#[cfg(not(any(test, feature = "test-support")))]
|
||||||
|
@ -1891,73 +1849,11 @@ impl RepoSnapshot {
|
||||||
|
|
||||||
Ok(flushed_operations)
|
Ok(flushed_operations)
|
||||||
} else {
|
} else {
|
||||||
for parent in operation.parent().iter() {
|
self.history.defer(operation, kv).await?;
|
||||||
self.deferred_operations.insert_or_replace(
|
|
||||||
DeferredOperation {
|
|
||||||
parent: *parent,
|
|
||||||
operation: operation.clone(),
|
|
||||||
},
|
|
||||||
&(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove any operations deferred on the given parent and add them to the
|
|
||||||
/// provided operation queue. This is called in `apply_operations`.
|
|
||||||
async fn flush_deferred_operations(
|
|
||||||
&mut self,
|
|
||||||
parent_id: OperationId,
|
|
||||||
kv: &dyn KvStore,
|
|
||||||
) -> Result<SmallVec<[Operation; 1]>> {
|
|
||||||
self.deferred_operations
|
|
||||||
.load(kv, &(), |probe| {
|
|
||||||
let key_range = (
|
|
||||||
Bound::Excluded(*probe.start),
|
|
||||||
Bound::Included(*probe.summary),
|
|
||||||
);
|
|
||||||
key_range.contains(&parent_id)
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
let mut cursor = self.deferred_operations.cursor::<OperationId>();
|
|
||||||
let mut remaining = cursor.slice(&parent_id, Bias::Left, &());
|
|
||||||
let mut flushed = SmallVec::new();
|
|
||||||
flushed.extend(
|
|
||||||
cursor
|
|
||||||
.slice(&parent_id, Bias::Right, &())
|
|
||||||
.iter()
|
|
||||||
.map(|deferred| deferred.operation.clone()),
|
|
||||||
);
|
|
||||||
remaining.append(cursor.suffix(&()), &());
|
|
||||||
drop(cursor);
|
|
||||||
self.deferred_operations = remaining;
|
|
||||||
Ok(flushed)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn save_remote_operation(
|
|
||||||
&mut self,
|
|
||||||
operation: Operation,
|
|
||||||
kv: &dyn KvStore,
|
|
||||||
) -> Result<()> {
|
|
||||||
let replica_id = operation.id().replica_id;
|
|
||||||
let count = operation.id().operation_count;
|
|
||||||
if self.max_operation_ids.load(&replica_id, kv).await?.copied() < Some(count) {
|
|
||||||
self.max_operation_ids.insert(replica_id, count);
|
|
||||||
}
|
|
||||||
self.last_operation_id.observe(operation.id());
|
|
||||||
self.operations.store(operation.id(), operation, kv).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn operation(
|
|
||||||
&mut self,
|
|
||||||
operation_id: OperationId,
|
|
||||||
kv: &dyn KvStore,
|
|
||||||
) -> Result<Option<&Operation>> {
|
|
||||||
self.operations.load(&operation_id, kv).await
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cached_revision(&self, revision_id: &RevisionId) -> Result<Revision> {
|
fn cached_revision(&self, revision_id: &RevisionId) -> Result<Revision> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.revisions
|
.revisions
|
||||||
|
@ -1973,123 +1869,124 @@ impl RepoSnapshot {
|
||||||
revision_id: &RevisionId,
|
revision_id: &RevisionId,
|
||||||
kv: &dyn KvStore,
|
kv: &dyn KvStore,
|
||||||
) -> Result<Revision> {
|
) -> Result<Revision> {
|
||||||
// First, check if we have a revision cached for this revision id.
|
todo!()
|
||||||
// If not, we'll need to reconstruct it from a previous revision.
|
// // First, check if we have a revision cached for this revision id.
|
||||||
// We need to find a cached revision that is an ancestor of the given revision id.
|
// // If not, we'll need to reconstruct it from a previous revision.
|
||||||
// Once we find it, we must apply all ancestors of the given revision id that are not contained in the cached revision.
|
// // We need to find a cached revision that is an ancestor of the given revision id.
|
||||||
let mut revision = self.revisions.lock().get(revision_id).cloned();
|
// // Once we find it, we must apply all ancestors of the given revision id that are not contained in the cached revision.
|
||||||
if revision.is_none() {
|
// let mut revision = self.revisions.lock().get(revision_id).cloned();
|
||||||
revision = Revision::load(self.id, revision_id, kv).await.ok();
|
// if revision.is_none() {
|
||||||
if let Some(revision) = revision.as_ref() {
|
// revision = Revision::load(self.id, revision_id, kv).await.ok();
|
||||||
self.revisions
|
// if let Some(revision) = revision.as_ref() {
|
||||||
.lock()
|
// self.revisions
|
||||||
.entry(revision_id.clone())
|
// .lock()
|
||||||
.or_insert_with(|| revision.clone());
|
// .entry(revision_id.clone())
|
||||||
}
|
// .or_insert_with(|| revision.clone());
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
if let Some(revision) = revision {
|
// if let Some(revision) = revision {
|
||||||
Ok(revision)
|
// Ok(revision)
|
||||||
} else {
|
// } else {
|
||||||
struct Search {
|
// struct Search {
|
||||||
start: OperationId,
|
// start: OperationId,
|
||||||
ancestor: RevisionId,
|
// ancestor: RevisionId,
|
||||||
}
|
// }
|
||||||
|
|
||||||
let mut ancestors = HashMap::<RevisionId, HashSet<OperationId>>::default();
|
// let mut ancestors = HashMap::<RevisionId, HashSet<OperationId>>::default();
|
||||||
let mut searches = VecDeque::new();
|
// let mut searches = VecDeque::new();
|
||||||
let mut operations = BTreeSet::new();
|
// let mut operations = BTreeSet::new();
|
||||||
for operation_id in revision_id.iter() {
|
// for operation_id in revision_id.iter() {
|
||||||
operations.insert((operation_id.operation_count, operation_id.replica_id));
|
// operations.insert((operation_id.operation_count, operation_id.replica_id));
|
||||||
searches.push_back(Search {
|
// searches.push_back(Search {
|
||||||
start: *operation_id,
|
// start: *operation_id,
|
||||||
ancestor: self
|
// ancestor: self
|
||||||
.operation(*operation_id, kv)
|
// .operation(*operation_id, kv)
|
||||||
.await?
|
// .await?
|
||||||
.ok_or_else(|| anyhow!("operation {:?} not found", operation_id))?
|
// .ok_or_else(|| anyhow!("operation {:?} not found", operation_id))?
|
||||||
.parent()
|
// .parent()
|
||||||
.clone(),
|
// .clone(),
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
let mut common_ancestor_revision = Revision::default();
|
// let mut common_ancestor_revision = Revision::default();
|
||||||
let mut missing_operations_start = (OperationCount::default(), ReplicaId::default());
|
// let mut missing_operations_start = (OperationCount::default(), ReplicaId::default());
|
||||||
while let Some(search) = searches.pop_front() {
|
// while let Some(search) = searches.pop_front() {
|
||||||
let reachable_from = ancestors.entry(search.ancestor.clone()).or_default();
|
// let reachable_from = ancestors.entry(search.ancestor.clone()).or_default();
|
||||||
reachable_from.insert(search.start);
|
// reachable_from.insert(search.start);
|
||||||
|
|
||||||
// If the current revision is reachable from every operation in the original
|
// // If the current revision is reachable from every operation in the original
|
||||||
// revision id, it's a common ancestor.
|
// // revision id, it's a common ancestor.
|
||||||
if reachable_from.len() == revision_id.len() {
|
// if reachable_from.len() == revision_id.len() {
|
||||||
// We've found a common ancestor, now we load a revision for it. For it to
|
// // We've found a common ancestor, now we load a revision for it. For it to
|
||||||
// be a common ancestor means that all its downstream operations must
|
// // be a common ancestor means that all its downstream operations must
|
||||||
// have causally happened after it. Therefore, we should be able to
|
// // have causally happened after it. Therefore, we should be able to
|
||||||
// use the maximum lamport timestamp in the common ancestor's revision
|
// // use the maximum lamport timestamp in the common ancestor's revision
|
||||||
// and select only those operations we've found in the backwards search
|
// // and select only those operations we've found in the backwards search
|
||||||
// which have a higher lamport timestamp.
|
// // which have a higher lamport timestamp.
|
||||||
let revision = self.load_revision(&search.ancestor, kv).await?;
|
// let revision = self.load_revision(&search.ancestor, kv).await?;
|
||||||
common_ancestor_revision = revision.clone();
|
// common_ancestor_revision = revision.clone();
|
||||||
if let Some(max_operation_count) = search
|
// if let Some(max_operation_count) = search
|
||||||
.ancestor
|
// .ancestor
|
||||||
.iter()
|
// .iter()
|
||||||
.map(|operation_id| operation_id.operation_count)
|
// .map(|operation_id| operation_id.operation_count)
|
||||||
.max()
|
// .max()
|
||||||
{
|
// {
|
||||||
missing_operations_start = (
|
// missing_operations_start = (
|
||||||
OperationCount(max_operation_count.0 + 1),
|
// OperationCount(max_operation_count.0 + 1),
|
||||||
ReplicaId::default(),
|
// ReplicaId::default(),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
for operation_id in search.ancestor.iter() {
|
// for operation_id in search.ancestor.iter() {
|
||||||
operations.insert((operation_id.operation_count, operation_id.replica_id));
|
// operations.insert((operation_id.operation_count, operation_id.replica_id));
|
||||||
searches.push_back(Search {
|
// searches.push_back(Search {
|
||||||
start: search.start,
|
// start: search.start,
|
||||||
ancestor: self
|
// ancestor: self
|
||||||
.operation(*operation_id, kv)
|
// .operation(*operation_id, kv)
|
||||||
.await?
|
// .await?
|
||||||
.expect("operation must exist")
|
// .expect("operation must exist")
|
||||||
.parent()
|
// .parent()
|
||||||
.clone(),
|
// .clone(),
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Apply all the missing operations to the found revision.
|
// // Apply all the missing operations to the found revision.
|
||||||
for (operation_count, replica_id) in operations.range(missing_operations_start..) {
|
// for (operation_count, replica_id) in operations.range(missing_operations_start..) {
|
||||||
let missing_operation_id = OperationId {
|
// let missing_operation_id = OperationId {
|
||||||
replica_id: *replica_id,
|
// replica_id: *replica_id,
|
||||||
operation_count: *operation_count,
|
// operation_count: *operation_count,
|
||||||
};
|
// };
|
||||||
match self
|
// match self
|
||||||
.operation(missing_operation_id, kv)
|
// .operation(missing_operation_id, kv)
|
||||||
.await?
|
// .await?
|
||||||
.expect("operation must exist")
|
// .expect("operation must exist")
|
||||||
.clone()
|
// .clone()
|
||||||
{
|
// {
|
||||||
Operation::CreateDocument(op) => {
|
// Operation::CreateDocument(op) => {
|
||||||
op.apply(&mut common_ancestor_revision);
|
// op.apply(&mut common_ancestor_revision);
|
||||||
}
|
// }
|
||||||
Operation::Edit(op) => {
|
// Operation::Edit(op) => {
|
||||||
let parent_revision = self.load_revision(&op.parent, kv).await?;
|
// let parent_revision = self.load_revision(&op.parent, kv).await?;
|
||||||
op.apply(&parent_revision, &mut common_ancestor_revision)?;
|
// op.apply(&parent_revision, &mut common_ancestor_revision)?;
|
||||||
}
|
// }
|
||||||
Operation::CreateBranch(_) => {
|
// Operation::CreateBranch(_) => {
|
||||||
// Creating a branch doesn't have an impact on the revision, so we
|
// // Creating a branch doesn't have an impact on the revision, so we
|
||||||
// can ignore it.
|
// // can ignore it.
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
self.revisions
|
// self.revisions
|
||||||
.lock()
|
// .lock()
|
||||||
.entry(revision_id.clone())
|
// .entry(revision_id.clone())
|
||||||
.or_insert_with(|| common_ancestor_revision.clone());
|
// .or_insert_with(|| common_ancestor_revision.clone());
|
||||||
Ok(common_ancestor_revision)
|
// Ok(common_ancestor_revision)
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,39 +1,204 @@
|
||||||
|
use std::{cmp::Ordering, ops::RangeBounds};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
btree::{self, KvStore},
|
btree::{self, Bias, KvStore, SavedId},
|
||||||
messages::Operation,
|
messages::Operation,
|
||||||
OperationCount, OperationId, ReplicaId, RevisionId,
|
OperationCount, OperationId, ReplicaId, RevisionId,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use collections::{BTreeSet, HashMap, HashSet, VecDeque};
|
use collections::{BTreeSet, Bound, HashMap, HashSet, VecDeque};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
struct History {
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct SavedHistory {
|
||||||
|
operations: SavedId,
|
||||||
|
next_operation_id: OperationId,
|
||||||
|
max_operation_ids: SavedId,
|
||||||
|
deferred_operations: SavedId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct History {
|
||||||
operations: btree::Map<OperationId, Operation>,
|
operations: btree::Map<OperationId, Operation>,
|
||||||
next_operation_id: OperationId,
|
next_operation_id: OperationId,
|
||||||
|
max_operation_ids: btree::Map<ReplicaId, OperationCount>,
|
||||||
|
deferred_operations: btree::Sequence<DeferredOperation>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl History {
|
impl History {
|
||||||
fn new(replica_id: ReplicaId) -> Self {
|
pub fn new(replica_id: ReplicaId) -> Self {
|
||||||
Self {
|
Self {
|
||||||
operations: Default::default(),
|
operations: Default::default(),
|
||||||
next_operation_id: OperationId::new(replica_id),
|
next_operation_id: OperationId::new(replica_id),
|
||||||
|
max_operation_ids: Default::default(),
|
||||||
|
deferred_operations: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_operation_id(&mut self) -> OperationId {
|
pub fn ptr_eq(&self, other: &Self) -> bool {
|
||||||
|
btree::Map::ptr_eq(&self.operations, &other.operations)
|
||||||
|
&& btree::Map::ptr_eq(&self.max_operation_ids, &other.max_operation_ids)
|
||||||
|
&& btree::Sequence::ptr_eq(&self.deferred_operations, &other.deferred_operations)
|
||||||
|
&& self.next_operation_id == other.next_operation_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn load(saved_history: SavedHistory, kv: &dyn KvStore) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
operations: btree::Map::load_root(saved_history.operations, kv).await?,
|
||||||
|
next_operation_id: saved_history.next_operation_id,
|
||||||
|
max_operation_ids: btree::Map::load_all(saved_history.max_operation_ids, kv).await?,
|
||||||
|
deferred_operations: btree::Sequence::load_root(saved_history.deferred_operations, kv)
|
||||||
|
.await?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn save(&self, kv: &dyn KvStore) -> Result<SavedHistory> {
|
||||||
|
Ok(SavedHistory {
|
||||||
|
operations: self.operations.save(kv).await?,
|
||||||
|
next_operation_id: self.next_operation_id,
|
||||||
|
max_operation_ids: self.max_operation_ids.save(kv).await?,
|
||||||
|
deferred_operations: self.deferred_operations.save(kv).await?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_operation_id(&mut self) -> OperationId {
|
||||||
self.next_operation_id.tick()
|
self.next_operation_id.tick()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn insert(&mut self, operation: Operation, kv: &dyn KvStore) -> Result<()> {
|
pub fn max_operation_ids(&self) -> &btree::Map<ReplicaId, OperationCount> {
|
||||||
|
&self.max_operation_ids
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn insert(
|
||||||
|
&mut self,
|
||||||
|
operation: Operation,
|
||||||
|
kv: &dyn KvStore,
|
||||||
|
) -> Result<SmallVec<[Operation; 1]>> {
|
||||||
|
let op_id = operation.id();
|
||||||
|
self.next_operation_id.observe(op_id);
|
||||||
|
if self
|
||||||
|
.max_operation_ids
|
||||||
|
.load(&op_id.replica_id, kv)
|
||||||
|
.await?
|
||||||
|
.copied()
|
||||||
|
< Some(op_id.operation_count)
|
||||||
|
{
|
||||||
|
self.max_operation_ids
|
||||||
|
.insert(op_id.replica_id, op_id.operation_count);
|
||||||
|
}
|
||||||
|
self.operations.store(op_id, operation, kv).await?;
|
||||||
|
|
||||||
|
self.deferred_operations
|
||||||
|
.load(kv, &(), |probe| {
|
||||||
|
let key_range = (
|
||||||
|
Bound::Excluded(*probe.start),
|
||||||
|
Bound::Included(*probe.summary),
|
||||||
|
);
|
||||||
|
key_range.contains(&op_id)
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
let mut cursor = self.deferred_operations.cursor::<OperationId>();
|
||||||
|
let mut remaining = cursor.slice(&op_id, Bias::Left, &());
|
||||||
|
let mut flushed = SmallVec::new();
|
||||||
|
flushed.extend(
|
||||||
|
cursor
|
||||||
|
.slice(&op_id, Bias::Right, &())
|
||||||
|
.iter()
|
||||||
|
.map(|deferred| deferred.operation.clone()),
|
||||||
|
);
|
||||||
|
remaining.append(cursor.suffix(&()), &());
|
||||||
|
drop(cursor);
|
||||||
|
self.deferred_operations = remaining;
|
||||||
|
Ok(flushed)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_local(&mut self, operation: Operation) {
|
||||||
|
let id = operation.id();
|
||||||
self.next_operation_id.observe(operation.id());
|
self.next_operation_id.observe(operation.id());
|
||||||
self.operations.store(operation.id(), operation, kv).await?;
|
self.max_operation_ids
|
||||||
|
.insert(id.replica_id, id.operation_count);
|
||||||
|
self.operations.insert(id, operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn defer(&mut self, operation: Operation, kv: &dyn KvStore) -> Result<()> {
|
||||||
|
for parent in operation.parent().iter() {
|
||||||
|
self.deferred_operations
|
||||||
|
.load(kv, &(), |probe| {
|
||||||
|
let key_range = (
|
||||||
|
Bound::Excluded(*probe.start),
|
||||||
|
Bound::Included(*probe.summary),
|
||||||
|
);
|
||||||
|
key_range.contains(&operation.id())
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
self.deferred_operations.insert_or_replace(
|
||||||
|
DeferredOperation {
|
||||||
|
parent: *parent,
|
||||||
|
operation: operation.clone(),
|
||||||
|
},
|
||||||
|
&(),
|
||||||
|
);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn operation(&mut self, id: OperationId, kv: &dyn KvStore) -> Result<Option<&Operation>> {
|
pub async fn can_apply(&mut self, operation: &Operation, kv: &dyn KvStore) -> Result<bool> {
|
||||||
|
for parent in operation.parent().iter() {
|
||||||
|
if self.operations.load(parent, kv).await?.is_none() {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn has_applied(&mut self, operation: &Operation, kv: &dyn KvStore) -> Result<bool> {
|
||||||
|
Ok(self.operations.load(&operation.id(), kv).await?.is_some())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn operation(
|
||||||
|
&mut self,
|
||||||
|
id: OperationId,
|
||||||
|
kv: &dyn KvStore,
|
||||||
|
) -> Result<Option<&Operation>> {
|
||||||
self.operations.load(&id, kv).await
|
self.operations.load(&id, kv).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn traverse(&mut self, revision_id: &RevisionId, kv: &dyn KvStore) -> Result<Traversal> {
|
pub async fn operations_since(
|
||||||
|
&mut self,
|
||||||
|
version: &btree::Map<ReplicaId, OperationCount>,
|
||||||
|
kv: &dyn KvStore,
|
||||||
|
) -> Result<Vec<Operation>> {
|
||||||
|
let mut new_operations = Vec::new();
|
||||||
|
for (replica_id, end_op_count) in self.max_operation_ids.iter() {
|
||||||
|
let start_op = OperationId {
|
||||||
|
replica_id: *replica_id,
|
||||||
|
operation_count: version
|
||||||
|
.get(&replica_id)
|
||||||
|
.map(|count| OperationCount(count.0 + 1))
|
||||||
|
.unwrap_or_default(),
|
||||||
|
};
|
||||||
|
let end_op = OperationId {
|
||||||
|
replica_id: *replica_id,
|
||||||
|
operation_count: *end_op_count,
|
||||||
|
};
|
||||||
|
|
||||||
|
new_operations.extend(
|
||||||
|
self.operations
|
||||||
|
.load_from(&start_op, kv)
|
||||||
|
.await?
|
||||||
|
.take_while(|(op_id, _)| **op_id <= end_op)
|
||||||
|
.map(|(_, op)| op.clone()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(new_operations)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn traverse(
|
||||||
|
&mut self,
|
||||||
|
revision_id: &RevisionId,
|
||||||
|
kv: &dyn KvStore,
|
||||||
|
) -> Result<Traversal> {
|
||||||
let mut frontier = VecDeque::new();
|
let mut frontier = VecDeque::new();
|
||||||
let mut traversed = BTreeSet::new();
|
let mut traversed = BTreeSet::new();
|
||||||
for operation_id in revision_id.iter() {
|
for operation_id in revision_id.iter() {
|
||||||
|
@ -59,8 +224,8 @@ impl History {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Traversal<'a> {
|
pub struct Traversal<'a> {
|
||||||
history: &'a mut History,
|
pub history: &'a mut History,
|
||||||
frontier: VecDeque<Frontier>,
|
frontier: VecDeque<Frontier>,
|
||||||
traversed: BTreeSet<(OperationCount, ReplicaId)>,
|
traversed: BTreeSet<(OperationCount, ReplicaId)>,
|
||||||
ancestors: HashMap<RevisionId, HashSet<OperationId>>,
|
ancestors: HashMap<RevisionId, HashSet<OperationId>>,
|
||||||
|
@ -68,7 +233,7 @@ struct Traversal<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Traversal<'_> {
|
impl Traversal<'_> {
|
||||||
async fn next(&mut self, kv: &dyn KvStore) -> Result<Option<TraversalResult>> {
|
pub async fn next(&mut self, kv: &dyn KvStore) -> Result<Option<TraversalAncestor>> {
|
||||||
while let Some(frontier) = self.frontier.pop_front() {
|
while let Some(frontier) = self.frontier.pop_front() {
|
||||||
let reachable_from = self.ancestors.entry(frontier.revision.clone()).or_default();
|
let reachable_from = self.ancestors.entry(frontier.revision.clone()).or_default();
|
||||||
reachable_from.insert(frontier.source);
|
reachable_from.insert(frontier.source);
|
||||||
|
@ -110,7 +275,7 @@ impl Traversal<'_> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(Some(TraversalResult {
|
return Ok(Some(TraversalAncestor {
|
||||||
revision: frontier.revision,
|
revision: frontier.revision,
|
||||||
operations,
|
operations,
|
||||||
}));
|
}));
|
||||||
|
@ -142,9 +307,53 @@ struct Frontier {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Debug)]
|
#[derive(Eq, PartialEq, Debug)]
|
||||||
struct TraversalResult {
|
pub struct TraversalAncestor {
|
||||||
revision: RevisionId,
|
pub revision: RevisionId,
|
||||||
operations: BTreeSet<OperationId>,
|
pub operations: BTreeSet<OperationId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
struct DeferredOperation {
|
||||||
|
parent: OperationId,
|
||||||
|
operation: Operation,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for DeferredOperation {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.parent == other.parent && self.operation.id() == other.operation.id()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for DeferredOperation {}
|
||||||
|
|
||||||
|
impl PartialOrd for DeferredOperation {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for DeferredOperation {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
self.parent
|
||||||
|
.cmp(&other.parent)
|
||||||
|
.then_with(|| self.operation.id().cmp(&other.operation.id()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl btree::Item for DeferredOperation {
|
||||||
|
type Summary = OperationId;
|
||||||
|
|
||||||
|
fn summary(&self) -> Self::Summary {
|
||||||
|
self.parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl btree::KeyedItem for DeferredOperation {
|
||||||
|
type Key = (OperationId, OperationId);
|
||||||
|
|
||||||
|
fn key(&self) -> Self::Key {
|
||||||
|
(self.parent, self.operation.id())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -156,104 +365,104 @@ mod tests {
|
||||||
async fn test_traversal() {
|
async fn test_traversal() {
|
||||||
let kv = InMemoryKv::default();
|
let kv = InMemoryKv::default();
|
||||||
let mut history = History::new(ReplicaId(0));
|
let mut history = History::new(ReplicaId(0));
|
||||||
let op0 = insert_operation(&[], &mut history, &kv).await;
|
let op1 = insert_operation(&[], &mut history, &kv).await;
|
||||||
let op1 = insert_operation(&[op0.id()], &mut history, &kv).await;
|
let op2 = insert_operation(&[op1.id()], &mut history, &kv).await;
|
||||||
let op2 = insert_operation(&[op0.id()], &mut history, &kv).await;
|
let op3 = insert_operation(&[op1.id()], &mut history, &kv).await;
|
||||||
let op3 = insert_operation(&[op1.id(), op2.id()], &mut history, &kv).await;
|
let op4 = insert_operation(&[op2.id(), op3.id()], &mut history, &kv).await;
|
||||||
let op4 = insert_operation(&[op3.id()], &mut history, &kv).await;
|
let op5 = insert_operation(&[op4.id()], &mut history, &kv).await;
|
||||||
let op5 = insert_operation(&[op3.id()], &mut history, &kv).await;
|
let op6 = insert_operation(&[op4.id()], &mut history, &kv).await;
|
||||||
let op6 = insert_operation(&[op3.id(), op2.id()], &mut history, &kv).await;
|
let op7 = insert_operation(&[op4.id(), op3.id()], &mut history, &kv).await;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
traversal(&[op3.id()], &mut history, &kv).await,
|
traversal(&[op4.id()], &mut history, &kv).await,
|
||||||
&[
|
&[
|
||||||
TraversalResult {
|
TraversalAncestor {
|
||||||
revision: RevisionId::from([op1.id(), op2.id()].as_slice()),
|
revision: RevisionId::from([op2.id(), op3.id()].as_slice()),
|
||||||
operations: BTreeSet::from_iter([op3.id()]),
|
operations: BTreeSet::from_iter([op4.id()]),
|
||||||
},
|
},
|
||||||
TraversalResult {
|
TraversalAncestor {
|
||||||
revision: RevisionId::from([op0.id()].as_slice()),
|
revision: RevisionId::from([op1.id()].as_slice()),
|
||||||
operations: BTreeSet::from_iter([op1.id(), op2.id()]),
|
operations: BTreeSet::from_iter([op2.id(), op3.id()]),
|
||||||
},
|
},
|
||||||
TraversalResult {
|
TraversalAncestor {
|
||||||
revision: RevisionId::from([].as_slice()),
|
revision: RevisionId::from([].as_slice()),
|
||||||
operations: BTreeSet::from_iter([op0.id()]),
|
operations: BTreeSet::from_iter([op1.id()]),
|
||||||
}
|
|
||||||
]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
traversal(&[op5.id()], &mut history, &kv).await,
|
|
||||||
&[
|
|
||||||
TraversalResult {
|
|
||||||
revision: RevisionId::from([op3.id()].as_slice()),
|
|
||||||
operations: BTreeSet::from_iter([op5.id()]),
|
|
||||||
},
|
|
||||||
TraversalResult {
|
|
||||||
revision: RevisionId::from([op1.id(), op2.id()].as_slice()),
|
|
||||||
operations: BTreeSet::from_iter([op3.id()]),
|
|
||||||
},
|
|
||||||
TraversalResult {
|
|
||||||
revision: RevisionId::from([op0.id()].as_slice()),
|
|
||||||
operations: BTreeSet::from_iter([op1.id(), op2.id()]),
|
|
||||||
},
|
|
||||||
TraversalResult {
|
|
||||||
revision: RevisionId::from([].as_slice()),
|
|
||||||
operations: BTreeSet::from_iter([op0.id()]),
|
|
||||||
}
|
|
||||||
]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
traversal(&[op4.id(), op5.id()], &mut history, &kv).await,
|
|
||||||
&[
|
|
||||||
TraversalResult {
|
|
||||||
revision: RevisionId::from([op3.id()].as_slice()),
|
|
||||||
operations: BTreeSet::from_iter([op4.id(), op5.id()]),
|
|
||||||
},
|
|
||||||
TraversalResult {
|
|
||||||
revision: RevisionId::from([op1.id(), op2.id()].as_slice()),
|
|
||||||
operations: BTreeSet::from_iter([op3.id()]),
|
|
||||||
},
|
|
||||||
TraversalResult {
|
|
||||||
revision: RevisionId::from([op0.id()].as_slice()),
|
|
||||||
operations: BTreeSet::from_iter([op1.id(), op2.id()]),
|
|
||||||
},
|
|
||||||
TraversalResult {
|
|
||||||
revision: RevisionId::from([].as_slice()),
|
|
||||||
operations: BTreeSet::from_iter([op0.id()]),
|
|
||||||
}
|
|
||||||
]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
traversal(&[op3.id(), op4.id()], &mut history, &kv).await,
|
|
||||||
&[
|
|
||||||
TraversalResult {
|
|
||||||
revision: RevisionId::from([op1.id(), op2.id()].as_slice()),
|
|
||||||
operations: BTreeSet::from_iter([op3.id(), op4.id()]),
|
|
||||||
},
|
|
||||||
TraversalResult {
|
|
||||||
revision: RevisionId::from([op0.id()].as_slice()),
|
|
||||||
operations: BTreeSet::from_iter([op1.id(), op2.id()]),
|
|
||||||
},
|
|
||||||
TraversalResult {
|
|
||||||
revision: RevisionId::from([].as_slice()),
|
|
||||||
operations: BTreeSet::from_iter([op0.id()]),
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
traversal(&[op6.id()], &mut history, &kv).await,
|
traversal(&[op6.id()], &mut history, &kv).await,
|
||||||
&[
|
&[
|
||||||
TraversalResult {
|
TraversalAncestor {
|
||||||
revision: RevisionId::from([op3.id(), op2.id()].as_slice()),
|
revision: RevisionId::from([op4.id()].as_slice()),
|
||||||
operations: BTreeSet::from_iter([op6.id()]),
|
operations: BTreeSet::from_iter([op6.id()]),
|
||||||
},
|
},
|
||||||
TraversalResult {
|
TraversalAncestor {
|
||||||
revision: RevisionId::from([op0.id()].as_slice()),
|
revision: RevisionId::from([op2.id(), op3.id()].as_slice()),
|
||||||
operations: BTreeSet::from_iter([op1.id(), op2.id(), op3.id()]),
|
operations: BTreeSet::from_iter([op4.id()]),
|
||||||
},
|
},
|
||||||
TraversalResult {
|
TraversalAncestor {
|
||||||
|
revision: RevisionId::from([op1.id()].as_slice()),
|
||||||
|
operations: BTreeSet::from_iter([op2.id(), op3.id()]),
|
||||||
|
},
|
||||||
|
TraversalAncestor {
|
||||||
revision: RevisionId::from([].as_slice()),
|
revision: RevisionId::from([].as_slice()),
|
||||||
operations: BTreeSet::from_iter([op0.id()]),
|
operations: BTreeSet::from_iter([op1.id()]),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
traversal(&[op5.id(), op6.id()], &mut history, &kv).await,
|
||||||
|
&[
|
||||||
|
TraversalAncestor {
|
||||||
|
revision: RevisionId::from([op4.id()].as_slice()),
|
||||||
|
operations: BTreeSet::from_iter([op5.id(), op6.id()]),
|
||||||
|
},
|
||||||
|
TraversalAncestor {
|
||||||
|
revision: RevisionId::from([op2.id(), op3.id()].as_slice()),
|
||||||
|
operations: BTreeSet::from_iter([op4.id()]),
|
||||||
|
},
|
||||||
|
TraversalAncestor {
|
||||||
|
revision: RevisionId::from([op1.id()].as_slice()),
|
||||||
|
operations: BTreeSet::from_iter([op2.id(), op3.id()]),
|
||||||
|
},
|
||||||
|
TraversalAncestor {
|
||||||
|
revision: RevisionId::from([].as_slice()),
|
||||||
|
operations: BTreeSet::from_iter([op1.id()]),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
traversal(&[op4.id(), op5.id()], &mut history, &kv).await,
|
||||||
|
&[
|
||||||
|
TraversalAncestor {
|
||||||
|
revision: RevisionId::from([op2.id(), op3.id()].as_slice()),
|
||||||
|
operations: BTreeSet::from_iter([op4.id(), op5.id()]),
|
||||||
|
},
|
||||||
|
TraversalAncestor {
|
||||||
|
revision: RevisionId::from([op1.id()].as_slice()),
|
||||||
|
operations: BTreeSet::from_iter([op2.id(), op3.id()]),
|
||||||
|
},
|
||||||
|
TraversalAncestor {
|
||||||
|
revision: RevisionId::from([].as_slice()),
|
||||||
|
operations: BTreeSet::from_iter([op1.id()]),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
traversal(&[op7.id()], &mut history, &kv).await,
|
||||||
|
&[
|
||||||
|
TraversalAncestor {
|
||||||
|
revision: RevisionId::from([op4.id(), op3.id()].as_slice()),
|
||||||
|
operations: BTreeSet::from_iter([op7.id()]),
|
||||||
|
},
|
||||||
|
TraversalAncestor {
|
||||||
|
revision: RevisionId::from([op1.id()].as_slice()),
|
||||||
|
operations: BTreeSet::from_iter([op2.id(), op3.id(), op4.id()]),
|
||||||
|
},
|
||||||
|
TraversalAncestor {
|
||||||
|
revision: RevisionId::from([].as_slice()),
|
||||||
|
operations: BTreeSet::from_iter([op1.id()]),
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -277,7 +486,7 @@ mod tests {
|
||||||
revision_id: &[OperationId],
|
revision_id: &[OperationId],
|
||||||
history: &mut History,
|
history: &mut History,
|
||||||
kv: &dyn KvStore,
|
kv: &dyn KvStore,
|
||||||
) -> Vec<TraversalResult> {
|
) -> Vec<TraversalAncestor> {
|
||||||
let mut traversal = history.traverse(&revision_id.into(), kv).await.unwrap();
|
let mut traversal = history.traverse(&revision_id.into(), kv).await.unwrap();
|
||||||
let mut results = Vec::new();
|
let mut results = Vec::new();
|
||||||
while let Some(result) = traversal.next(kv).await.unwrap() {
|
while let Some(result) = traversal.next(kv).await.unwrap() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue