diff options
| author | tv <tv@krebsco.de> | 2026-11-01 13:19:04 +0100 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-01 13:19:04 +0100 |
| commit | 7a5a7408179c5fe9943bd2ce60295e86f1afaa40 (patch) | |
| tree | 166284a6b05f1f3e5d5166c38eda33088ae5461b /src/state/mod.rs | |
| parent | b4aa16f2a3c3f9dc0d4eb6e7399bd7ffe269d669 (diff) | |
document: reject writes during rejoin
Diffstat (limited to 'src/state/mod.rs')
| -rw-r--r-- | src/state/mod.rs | 12 |
1 files changed, 9 insertions, 3 deletions
diff --git a/src/state/mod.rs b/src/state/mod.rs index d8bdac5..9ee08b7 100644 --- a/src/state/mod.rs +++ b/src/state/mod.rs @@ -38,7 +38,7 @@ use tracing::{debug, error, warn}; use crate::document::lock::DocumentLock; use crate::document::replica::{Epoch, Stamp}; -use crate::document::store::ChangeSet; +use crate::document::store::{ChangeSet, StoreError}; use crate::document::{Document, Transaction}; use crate::locks::MutexExt; use crate::remake::{Host, RemakeState}; @@ -393,20 +393,26 @@ impl AppState { /// Mutates the document on the blocking pool as one change, then /// publishes it. Write and notification run in the same blocking task, so a - /// cancelled request can't leave a committed write unannounced. + /// cancelled request can't leave a committed write unannounced. Refused + /// (`StoreError::Rejoining`, `f` not called) while the document isn't writable. pub(crate) async fn mutate_doc<T, E>( &self, f: impl FnOnce(&mut Transaction<'_>) -> Result<T, E> + Send + 'static, ) -> Result<T, E> where T: Send + 'static, - E: Send + 'static, + E: From<StoreError> + Send + 'static, { let state = self.clone(); tokio::task::spawn_blocking(move || { let result = { let mut replica = state.doc.blocking_write(); + // Checked here, under the lock: the caller's readiness check (if any) + // was before a rejoin could swap in a fresh document. + if !replica.writable { + return Err(E::from(StoreError::Rejoining)); + } // One API write = one change; on error, its partial writes (e.g. the old // value deleted, the new one not written) are dropped. let result = replica.transact(state.clock.now_millis(), f); |
