aboutsummaryrefslogtreecommitdiffstats
path: root/src/state/mod.rs
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-01 19:35:20 +0100
committertv <tv@krebsco.de>2026-11-01 19:35:20 +0100
commit5640bacf735c6728acf4bc22217b0c07ac4bf7f9 (patch)
treee1cdf1a912ca90d24d6a8100263edc0c5298305e /src/state/mod.rs
parent2ac2b0e2b08f14be07c46b3fd9a516a64e9933fb (diff)
document: allow writes during rejoin
Diffstat (limited to 'src/state/mod.rs')
-rw-r--r--src/state/mod.rs21
1 files changed, 17 insertions, 4 deletions
diff --git a/src/state/mod.rs b/src/state/mod.rs
index 6e6e48c..7c1299f 100644
--- a/src/state/mod.rs
+++ b/src/state/mod.rs
@@ -40,7 +40,7 @@ use crate::document::lock::DocumentLock;
use crate::document::replica::{Epoch, Stamp};
use crate::document::store::{ChangeSet, StoreError};
use crate::document::{Document, Transaction};
-use crate::locks::MutexExt;
+use crate::locks::{MutexExt, RwLockExt};
use crate::remake::{Host, RemakeState};
use crate::swarm::sync::SyncEvent;
use crate::swarm::throughput::ThroughputTable;
@@ -394,8 +394,10 @@ 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. Refused
- /// (`StoreError::Rejoining`, `f` not called) while the document isn't writable.
+ /// cancelled request can't leave a committed write unannounced. While a rejoin runs,
+ /// the write goes into the document before it (`StaleView`), replayed when the
+ /// rejoin ends; on a node that keeps none, it is refused (`StoreError::Rejoining`,
+ /// `f` not called).
pub(crate) async fn mutate_doc<T, E>(
&self,
f: impl FnOnce(&mut Transaction<'_>) -> Result<T, E> + Send + 'static,
@@ -412,7 +414,18 @@ impl AppState {
// 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));
+ // Rejoining: into the document before the rejoin, which the rejoin
+ // replays when it ends (`StaleView`); refused by a node that keeps
+ // none (it wasn't ready).
+ let Some((_, stale)) = state.stale_document() else {
+ return Err(E::from(StoreError::Rejoining));
+ };
+ let mut doc = stale.write_or_recover();
+ return crate::document::replica::transact(
+ &mut doc,
+ state.clock.now_millis(),
+ f,
+ );
}
// One API write = one change; on error, its partial writes (e.g. the old
// value deleted, the new one not written) are dropped.