diff options
| author | tv <tv@krebsco.de> | 2026-10-31 19:53:27 +0100 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-10-31 19:53:27 +0100 |
| commit | 6105507200231e67ba544c12aa10b988e1c23826 (patch) | |
| tree | 880530b7401cb5d43f52e06a68b4d704326aa272 /src/document/replica.rs | |
| parent | 8d7adfbdb7798cca9d9783761f290e3bd85d9a3a (diff) | |
use Automerge as Document instead of AutoCommit
This allows a lot of operations to drop mut.
Diffstat (limited to 'src/document/replica.rs')
| -rw-r--r-- | src/document/replica.rs | 53 |
1 files changed, 31 insertions, 22 deletions
diff --git a/src/document/replica.rs b/src/document/replica.rs index 5a87016..20c02e5 100644 --- a/src/document/replica.rs +++ b/src/document/replica.rs @@ -10,7 +10,7 @@ use std::fmt; use std::ops::{Deref, DerefMut}; use std::sync::Arc; -use automerge::{AutoCommit, ChangeHash}; +use super::{ChangeHash, Document, Transaction}; use tokio::sync::watch; /// One generation of the swarm document. @@ -95,12 +95,11 @@ impl fmt::Debug for Epoch { /// The document and its epoch, always locked and swapped together. /// -/// Derefs to the document, so code that only needs the `AutoCommit` keeps working -/// with a guard of `Replica`. +/// Derefs to the document, so code that only needs the document keeps working with a +/// guard of `Replica`. /// -/// The heads are cached: `AutoCommit::get_heads` takes `&mut self` (it closes a pending -/// transaction), so without the cache every keepalive and status read would need the -/// write lock. Every path that changes the document under the write lock calls +/// The heads are cached, sorted, so keepalives and status reads (and the stamp below) +/// don't recompute them. Every path that changes the document under the write lock calls /// [`Replica::refresh_heads`] before releasing it (`AppState::mutate_doc`, /// `switch_epoch`, the sync apply); readers take the read lock and use /// [`Replica::heads`]. @@ -113,7 +112,7 @@ pub(crate) struct Replica { /// How many times this node changed epoch (switch, rejoin, abandoned rejoin) in this /// run: messages carry it, so a late one can't flip a peer's view of our epoch back. pub(crate) epoch_seq: u32, - pub(crate) doc: AutoCommit, + pub(crate) doc: Document, heads: Vec<ChangeHash>, stamp: watch::Sender<Stamp>, } @@ -129,7 +128,7 @@ pub(crate) struct Stamp { } impl Replica { - pub(crate) fn new(epoch: Epoch, doc: AutoCommit) -> Self { + pub(crate) fn new(epoch: Epoch, doc: Document) -> Self { let (stamp, _) = watch::channel(Stamp { epoch, epoch_seq: 0, @@ -168,30 +167,40 @@ impl Replica { self.stamp.subscribe() } - /// The writes since the last commit, as one change stamped `at`: receivers tell by - /// it how long the change took to reach them (`swarm::stats`). - pub(crate) fn commit(&mut self, at: crate::clock::UnixMillis) { - self.doc.commit_with( - automerge::transaction::CommitOptions::default().with_time(at.as_u64() as i64), - ); - } - - /// The writes since the last commit, discarded. - pub(crate) fn rollback(&mut self) { - self.doc.rollback(); + /// The writes `f` makes, as one change stamped `at` if it succeeds (receivers tell + /// by the stamp how long the change took to reach them, `swarm::stats`), dropped if + /// not. Doesn't refresh the heads: the caller does, under the same lock. + pub(crate) fn transact<T, E>( + &mut self, + at: crate::clock::UnixMillis, + f: impl FnOnce(&mut Transaction<'_>) -> Result<T, E>, + ) -> Result<T, E> { + let mut transaction = self.doc.transaction(); + let out = f(&mut transaction); + match &out { + Ok(_) => { + transaction.commit_with( + automerge::transaction::CommitOptions::default().with_time(at.as_u64() as i64), + ); + } + Err(_) => { + transaction.rollback(); + } + } + out } } impl Deref for Replica { - type Target = AutoCommit; + type Target = Document; - fn deref(&self) -> &AutoCommit { + fn deref(&self) -> &Document { &self.doc } } impl DerefMut for Replica { - fn deref_mut(&mut self) -> &mut AutoCommit { + fn deref_mut(&mut self) -> &mut Document { &mut self.doc } } |
