cob: Have clock value for empty COB state
By using `0` as the clock value for an empty state (without operations), we simplify transaction code. Previously, there was no clock value for empty states, which meant that we had to sometimes use an `Option`.
This commit is contained in:
parent
c568feb700
commit
5ee1f42b6a
|
|
@ -48,8 +48,8 @@ pub fn evaluate(root: Oid, graph: &Dag<Oid, Change>, rng: fastrand::Rng) -> hist
|
||||||
clock + entry.contents().len() as Clock - 1
|
clock + entry.contents().len() as Clock - 1
|
||||||
})
|
})
|
||||||
.max()
|
.max()
|
||||||
.map(|n| n + 1)
|
.unwrap_or_default() // When there are no operations, the clock is zero.
|
||||||
.unwrap_or_default();
|
+ 1;
|
||||||
log::trace!("change '{}' accepted", c.change.id());
|
log::trace!("change '{}' accepted", c.change.id());
|
||||||
|
|
||||||
entries.insert(*entry.id(), EntryWithClock { entry, clock });
|
entries.insert(*entry.id(), EntryWithClock { entry, clock });
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ impl History {
|
||||||
timestamp,
|
timestamp,
|
||||||
};
|
};
|
||||||
let mut entries = HashMap::new();
|
let mut entries = HashMap::new();
|
||||||
entries.insert(id, EntryWithClock::from(root_entry));
|
entries.insert(id, EntryWithClock::root(root_entry));
|
||||||
|
|
||||||
create_dag(&id, &entries)
|
create_dag(&id, &entries)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -131,11 +131,11 @@ pub struct EntryWithClock {
|
||||||
pub clock: Clock,
|
pub clock: Clock,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Entry> for EntryWithClock {
|
impl EntryWithClock {
|
||||||
fn from(entry: Entry) -> Self {
|
pub fn root(entry: Entry) -> Self {
|
||||||
Self {
|
Self {
|
||||||
entry,
|
entry,
|
||||||
clock: Clock::default(),
|
clock: 1 as Clock, // The root entry has a clock value of `1`.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -433,7 +433,7 @@ impl<'a> Issues<'a> {
|
||||||
tx.tag(tags.to_owned(), []);
|
tx.tag(tags.to_owned(), []);
|
||||||
})?;
|
})?;
|
||||||
// Just a sanity check that our clock is advancing as expected.
|
// Just a sanity check that our clock is advancing as expected.
|
||||||
debug_assert_eq!(clock.get(), 2);
|
debug_assert_eq!(clock.get(), 3);
|
||||||
|
|
||||||
Ok(IssueMut {
|
Ok(IssueMut {
|
||||||
id,
|
id,
|
||||||
|
|
@ -549,6 +549,8 @@ mod test {
|
||||||
let created = issues
|
let created = issues
|
||||||
.create("My first issue", "Blah blah blah.", &[], &signer)
|
.create("My first issue", "Blah blah blah.", &[], &signer)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
assert_eq!(created.clock().get(), 3);
|
||||||
|
|
||||||
let (id, created) = (created.id, created.issue);
|
let (id, created) = (created.id, created.issue);
|
||||||
let issue = issues.get(&id).unwrap().unwrap();
|
let issue = issues.get(&id).unwrap().unwrap();
|
||||||
|
|
||||||
|
|
@ -645,7 +647,7 @@ mod test {
|
||||||
let mut issue = issues
|
let mut issue = issues
|
||||||
.create("My first issue", "Blah blah blah.", &[], &signer)
|
.create("My first issue", "Blah blah blah.", &[], &signer)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let root = OpId::initial(author);
|
let root = OpId::root(author);
|
||||||
|
|
||||||
let c1 = issue.comment("Hi hi hi.", root, &signer).unwrap();
|
let c1 = issue.comment("Hi hi hi.", root, &signer).unwrap();
|
||||||
let c2 = issue.comment("Ha ha ha.", root, &signer).unwrap();
|
let c2 = issue.comment("Ha ha ha.", root, &signer).unwrap();
|
||||||
|
|
@ -706,8 +708,8 @@ mod test {
|
||||||
.create("My first issue", "Blah blah blah.", &[], &signer)
|
.create("My first issue", "Blah blah blah.", &[], &signer)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// The initial thread op id is always the same.
|
// The root thread op id is always the same.
|
||||||
let c0 = OpId::initial(author);
|
let c0 = OpId::root(author);
|
||||||
|
|
||||||
issue.comment("Ho ho ho.", c0, &signer).unwrap();
|
issue.comment("Ho ho ho.", c0, &signer).unwrap();
|
||||||
issue.comment("Ha ha ha.", c0, &signer).unwrap();
|
issue.comment("Ha ha ha.", c0, &signer).unwrap();
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,10 @@ impl OpId {
|
||||||
Self(Lamport::initial(), actor)
|
Self(Lamport::initial(), actor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn root(actor: ActorId) -> Self {
|
||||||
|
Self(Lamport::initial().tick(), actor)
|
||||||
|
}
|
||||||
|
|
||||||
/// Get operation id clock.
|
/// Get operation id clock.
|
||||||
pub fn clock(&self) -> Lamport {
|
pub fn clock(&self) -> Lamport {
|
||||||
self.0
|
self.0
|
||||||
|
|
@ -153,7 +157,7 @@ impl<G: Signer, A: Clone> Actor<G, A> {
|
||||||
/// Create a new operation.
|
/// Create a new operation.
|
||||||
pub fn op(&mut self, action: A) -> Op<A> {
|
pub fn op(&mut self, action: A) -> Op<A> {
|
||||||
let author = *self.signer.public_key();
|
let author = *self.signer.public_key();
|
||||||
let clock = self.clock;
|
let clock = self.clock.tick();
|
||||||
let timestamp = clock::Physical::now();
|
let timestamp = clock::Physical::now();
|
||||||
let op = Op {
|
let op = Op {
|
||||||
action,
|
action,
|
||||||
|
|
@ -162,7 +166,6 @@ impl<G: Signer, A: Clone> Actor<G, A> {
|
||||||
timestamp,
|
timestamp,
|
||||||
};
|
};
|
||||||
self.ops.insert((self.clock, author), op.clone());
|
self.ops.insert((self.clock, author), op.clone());
|
||||||
self.clock.tick();
|
|
||||||
|
|
||||||
op
|
op
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -787,7 +787,7 @@ impl<'a> Patches<'a> {
|
||||||
tx.tag(tags.to_owned(), []);
|
tx.tag(tags.to_owned(), []);
|
||||||
})?;
|
})?;
|
||||||
// Just a sanity check that our clock is advancing as expected.
|
// Just a sanity check that our clock is advancing as expected.
|
||||||
debug_assert_eq!(clock.get(), 2);
|
debug_assert_eq!(clock.get(), 3);
|
||||||
|
|
||||||
Ok(PatchMut::new(id, patch, clock, self))
|
Ok(PatchMut::new(id, patch, clock, self))
|
||||||
}
|
}
|
||||||
|
|
@ -1023,7 +1023,7 @@ mod test {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(patch.clock, clock::Lamport::from(2));
|
assert_eq!(patch.clock.get(), 3);
|
||||||
|
|
||||||
let id = patch.id;
|
let id = patch.id;
|
||||||
let patch = patches.get(&id).unwrap().unwrap();
|
let patch = patches.get(&id).unwrap().unwrap();
|
||||||
|
|
@ -1238,15 +1238,15 @@ mod test {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(patch.clock.get(), 2);
|
assert_eq!(patch.clock.get(), 3);
|
||||||
assert_eq!(patch.description(), Some("Blah blah blah."));
|
assert_eq!(patch.description(), Some("Blah blah blah."));
|
||||||
assert_eq!(patch.version(), 0);
|
assert_eq!(patch.version(), 0);
|
||||||
|
|
||||||
let (r1, t1) = patch
|
let (r1, t1) = patch
|
||||||
.update("I've made changes.", base, rev1_oid, &signer)
|
.update("I've made changes.", base, rev1_oid, &signer)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(r1.clock().get(), 3);
|
assert_eq!(r1.clock().get(), 4);
|
||||||
assert_eq!(t1.clock().get(), 4);
|
assert_eq!(t1.clock().get(), 5);
|
||||||
|
|
||||||
let id = patch.id;
|
let id = patch.id;
|
||||||
let patch = patches.get(&id).unwrap().unwrap();
|
let patch = patches.get(&id).unwrap().unwrap();
|
||||||
|
|
|
||||||
|
|
@ -194,17 +194,19 @@ where
|
||||||
pub struct Transaction<T: FromHistory> {
|
pub struct Transaction<T: FromHistory> {
|
||||||
actor: ActorId,
|
actor: ActorId,
|
||||||
start: Lamport,
|
start: Lamport,
|
||||||
clock: Option<Lamport>,
|
clock: Lamport,
|
||||||
actions: Vec<T::Action>,
|
actions: Vec<T::Action>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: FromHistory> Transaction<T> {
|
impl<T: FromHistory> Transaction<T> {
|
||||||
/// Create a new transaction.
|
/// Create a new transaction.
|
||||||
pub fn new(actor: ActorId, clock: Lamport) -> Self {
|
pub fn new(actor: ActorId, clock: Lamport) -> Self {
|
||||||
|
let start = clock;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
actor,
|
actor,
|
||||||
start: clock,
|
start,
|
||||||
clock: Some(clock),
|
clock,
|
||||||
actions: Vec::new(),
|
actions: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -225,7 +227,7 @@ impl<T: FromHistory> Transaction<T> {
|
||||||
let mut tx = Transaction {
|
let mut tx = Transaction {
|
||||||
actor,
|
actor,
|
||||||
start: Lamport::initial(),
|
start: Lamport::initial(),
|
||||||
clock: None,
|
clock: Lamport::initial(),
|
||||||
actions: Vec::new(),
|
actions: Vec::new(),
|
||||||
};
|
};
|
||||||
operations(&mut tx);
|
operations(&mut tx);
|
||||||
|
|
@ -235,7 +237,7 @@ impl<T: FromHistory> Transaction<T> {
|
||||||
let (id, cob, clock) = store.create(message, actions, signer)?;
|
let (id, cob, clock) = store.create(message, actions, signer)?;
|
||||||
|
|
||||||
// The history clock should be in sync with the tx clock.
|
// The history clock should be in sync with the tx clock.
|
||||||
assert_eq!(Some(clock), tx.clock);
|
assert_eq!(clock, tx.clock);
|
||||||
|
|
||||||
Ok((id, cob, clock))
|
Ok((id, cob, clock))
|
||||||
}
|
}
|
||||||
|
|
@ -243,20 +245,7 @@ impl<T: FromHistory> Transaction<T> {
|
||||||
/// Add an operation to this transaction.
|
/// Add an operation to this transaction.
|
||||||
pub fn push(&mut self, action: T::Action) -> cob::OpId {
|
pub fn push(&mut self, action: T::Action) -> cob::OpId {
|
||||||
self.actions.push(action);
|
self.actions.push(action);
|
||||||
|
OpId::new(self.clock.tick(), self.actor)
|
||||||
// If our clock already had a value, it means this isn't the first operation
|
|
||||||
// of this COB. In that case we 'tick' the clock and return the new clock
|
|
||||||
// value.
|
|
||||||
//
|
|
||||||
// Otherwise, it means it was the first operation of our COB. In that case
|
|
||||||
// we set our clock to the initial clock value (0), and return that.
|
|
||||||
if let Some(ref mut clock) = self.clock {
|
|
||||||
OpId::new(clock.tick(), self.actor)
|
|
||||||
} else {
|
|
||||||
self.clock = Some(Lamport::initial());
|
|
||||||
|
|
||||||
OpId::initial(self.actor)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Commit transaction.
|
/// Commit transaction.
|
||||||
|
|
@ -279,7 +268,7 @@ impl<T: FromHistory> Transaction<T> {
|
||||||
let timestamp = cob.history().timestamp().into();
|
let timestamp = cob.history().timestamp().into();
|
||||||
|
|
||||||
// The history clock should be in sync with the tx clock.
|
// The history clock should be in sync with the tx clock.
|
||||||
assert_eq!(Some(cob.history().clock()), self.clock.map(|c| c.get()));
|
assert_eq!(cob.history().clock(), self.clock.get());
|
||||||
|
|
||||||
// Start the clock from where the transcation clock started.
|
// Start the clock from where the transcation clock started.
|
||||||
let mut clock = self.start;
|
let mut clock = self.start;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue