diff options
| author | tv <tv@krebsco.de> | 2026-10-24 20:28:36 +0200 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-10-24 20:28:36 +0200 |
| commit | 59c1511bcacedddc354e82033d523f459c6db5d4 (patch) | |
| tree | fae84e91e1c7f4a98b2e99c79602ace059c6e211 /src/state.rs | |
| parent | 8e8aee67ef8dfa7c30a3734a4b6f5357c87f1a67 (diff) | |
cache heads
To reduce the number of write locks, heads are now cached.
Diffstat (limited to 'src/state.rs')
| -rw-r--r-- | src/state.rs | 33 |
1 files changed, 20 insertions, 13 deletions
diff --git a/src/state.rs b/src/state.rs index 9383c89..0dd3a81 100644 --- a/src/state.rs +++ b/src/state.rs @@ -815,9 +815,14 @@ impl AppState { T: Send + 'static, { let doc = Arc::clone(&self.doc); - tokio::task::spawn_blocking(move || f(&mut doc.blocking_write().doc)) - .await - .expect("document write task panicked") + tokio::task::spawn_blocking(move || { + let mut replica = doc.blocking_write(); + let out = f(&mut replica.doc); + replica.refresh_heads(); + out + }) + .await + .expect("document write task panicked") } /// Mutates the document on the blocking pool as one Automerge change, then @@ -852,6 +857,7 @@ impl AppState { replica.doc.rollback(); } } + replica.refresh_heads(); result }; // lock released before notifying @@ -944,8 +950,8 @@ impl AppState { /// anyway with `unready_live`. /// /// With `with_heads`, a live read also returns the document's heads at the moment of - /// the read (for clients that base later requests on what they read). That needs the - /// write lock, so it is opt-in. Stale reads have no heads. + /// the read (for clients that base later requests on what they read). Stale reads + /// have no heads. pub(crate) async fn render_read<T, R>( &self, unready_live: bool, @@ -995,14 +1001,14 @@ impl AppState { if !with_heads { return (self.render(read, finish).await, None); } - // AutoCommit::get_heads takes &mut self (it closes a pending transaction). - let (data, mut heads) = self - .write_doc(move |doc| { - let heads = doc.get_heads(); - (read(doc), heads) - }) - .await; - heads.sort(); + // Heads and data under the same read lock (the heads are cached: `Replica`). + let doc = Arc::clone(&self.doc); + let (data, heads) = tokio::task::spawn_blocking(move || { + let replica = doc.blocking_read(); + (read(&replica.doc), replica.heads().to_vec()) + }) + .await + .expect("document read task panicked"); let rendered = tokio::task::spawn_blocking(move || finish(data)) .await .expect("document read task panicked"); @@ -1198,6 +1204,7 @@ impl Host for AppState { let new_doc = f(&mut replica.doc)?; let old_doc = std::mem::replace(&mut replica.doc, new_doc); replica.epoch = epoch; + replica.refresh_heads(); // Updated under the same lock, so the cached epoch never disagrees // with the document for anyone holding the lock. state.epoch_tx.send_replace(epoch); |
