cob: Remove CRDTs from COB state

It turns out that the CRDT formed by the union of Git DAGs
is enough to guarantee everything we need for COBs.

This changes the following things:
* COB operations no longer need to be commutative
* COB histories are traversed in the same deterministic order on all
  replicas
* It's now possible to implement RSMs on top of COBs, eg. scripting
* Lamport clocks have been removed
* `radicle-crdt` is no longer a dependency of `radicle`
* COBs are no longer instances of `Semilattice`
* The `Ops` type was removed in favor of having `Op` contain multiple
  actions
This commit is contained in:
Alexis Sellier
2023-07-25 15:29:27 +02:00
parent 2e25250be4
commit 1dc799d679
3 changed files with 16 additions and 15 deletions
+6 -5
View File
@@ -138,7 +138,7 @@ pub(crate) fn patch(
"discussions": rev.discussion().comments()
.map(|(id, comment)| Comment::new(id, comment, aliases))
.collect::<Vec<_>>(),
"timestamp": rev.timestamp(),
"timestamp": rev.timestamp().as_secs().to_string(),
"reviews": rev.reviews().map(|(nid, _review)| review(nid, aliases.alias(nid), _review)).collect::<Vec<_>>(),
})
}).collect::<Vec<_>>(),
@@ -165,7 +165,7 @@ fn merge(merge: &Merge, nid: &NodeId, alias: Option<Alias>) -> Value {
"alias": alias,
},
"commit": merge.commit,
"timestamp": merge.timestamp,
"timestamp": merge.timestamp.as_secs().to_string(),
"revision": merge.revision,
}),
None => json!({
@@ -173,7 +173,7 @@ fn merge(merge: &Merge, nid: &NodeId, alias: Option<Alias>) -> Value {
"id": nid,
},
"commit": merge.commit,
"timestamp": merge.timestamp,
"timestamp": merge.timestamp.as_secs().to_string(),
"revision": merge.revision,
}),
}
@@ -190,7 +190,7 @@ fn review(nid: &NodeId, alias: Option<Alias>, review: &Review) -> Value {
"verdict": review.verdict(),
"summary": review.summary(),
"comments": review.comments().collect::<Vec<_>>(),
"timestamp": review.timestamp(),
"timestamp": review.timestamp().as_secs().to_string(),
}),
None => json!({
"author": {
@@ -199,7 +199,7 @@ fn review(nid: &NodeId, alias: Option<Alias>, review: &Review) -> Value {
"verdict": review.verdict(),
"summary": review.summary(),
"comments": review.comments().collect::<Vec<_>>(),
"timestamp": review.timestamp(),
"timestamp": review.timestamp().as_secs().to_string(),
}),
}
}
@@ -240,6 +240,7 @@ struct Comment<'a> {
author: Value,
body: &'a str,
reactions: Vec<(&'a ActorId, &'a Reaction)>,
#[serde(with = "radicle::serde_ext::localtime::time")]
timestamp: Timestamp,
reply_to: Option<CommentId>,
}
+8 -8
View File
@@ -460,16 +460,16 @@ async fn issues_handler(
let mut issues: Vec<_> = issues
.all()?
.filter_map(|r| {
let (id, issue, clock) = r.ok()?;
(state.matches(issue.state())).then_some((id, issue, clock))
let (id, issue) = r.ok()?;
(state.matches(issue.state())).then_some((id, issue))
})
.collect::<Vec<_>>();
issues.sort_by(|(_, a, _), (_, b, _)| b.timestamp().cmp(&a.timestamp()));
issues.sort_by(|(_, a), (_, b)| b.timestamp().cmp(&a.timestamp()));
let aliases = &ctx.profile.aliases();
let issues = issues
.into_iter()
.map(|(id, issue, _)| api::json::issue(id, issue, aliases))
.map(|(id, issue)| api::json::issue(id, issue, aliases))
.skip(page * per_page)
.take(per_page)
.collect::<Vec<_>>();
@@ -739,15 +739,15 @@ async fn patches_handler(
let mut patches = patches
.all()?
.filter_map(|r| {
let (id, patch, clock) = r.ok()?;
(state.matches(patch.state())).then_some((id, patch, clock))
let (id, patch) = r.ok()?;
(state.matches(patch.state())).then_some((id, patch))
})
.collect::<Vec<_>>();
patches.sort_by(|(_, a, _), (_, b, _)| b.timestamp().cmp(&a.timestamp()));
patches.sort_by(|(_, a), (_, b)| b.timestamp().cmp(&a.timestamp()));
let aliases = ctx.profile.aliases();
let patches = patches
.into_iter()
.map(|(id, patch, _)| api::json::patch(id, patch, &repo, &aliases))
.map(|(id, patch)| api::json::patch(id, patch, &repo, &aliases))
.skip(page * per_page)
.take(per_page)
.collect::<Vec<_>>();
+2 -2
View File
@@ -37,7 +37,7 @@ pub const ISSUE_ID: &str = "5ad77fa3f476beed9a26f49b2b3b844e61bef792";
pub const ISSUE_DISCUSSION_ID: &str = "f1dff128a22e8183a23516dd9812e72e80914c92";
pub const ISSUE_COMMENT_ID: &str = "845218041bf9eb8155bfa4aaa8f0c91ce18e5c13";
pub const SESSION_ID: &str = "u9MGAkkfkMOv0uDDB2WeUHBT7HbsO2Dy";
pub const TIMESTAMP: u64 = 1671125284;
pub const TIMESTAMP: &str = "1671125284";
pub const CONTRIBUTOR_RID: &str = "rad:z4XaCmN3jLSeiMvW15YTDpNbDHFhG";
pub const CONTRIBUTOR_DID: &str = "did:key:z6Mkk7oqY4pPxhMmGEotDYsFo97vhCj85BLY1H256HrJmjN8";
pub const CONTRIBUTOR_NID: &str = "z6Mkk7oqY4pPxhMmGEotDYsFo97vhCj85BLY1H256HrJmjN8";
@@ -97,7 +97,7 @@ fn seed_with_signer<G: Signer>(dir: &Path, profile: radicle::Profile, signer: &G
let workdir = dir.join("hello-world");
env::set_var("RAD_COMMIT_TIME", TIMESTAMP.to_string());
env::set_var("RAD_COMMIT_TIME", TIMESTAMP);
fs::create_dir_all(&workdir).unwrap();