diff options
Diffstat (limited to 'src/remake/switch.rs')
| -rw-r--r-- | src/remake/switch.rs | 32 |
1 files changed, 28 insertions, 4 deletions
diff --git a/src/remake/switch.rs b/src/remake/switch.rs index 2670f14..cf5ea75 100644 --- a/src/remake/switch.rs +++ b/src/remake/switch.rs @@ -24,14 +24,17 @@ use automerge::{ActorId, AutoCommit, ChangeHash}; use tokio::sync::broadcast; use tracing::{error, info, warn}; +use super::Host; use super::catchup; +use super::content; +use super::coordinator; use super::delta::{self, ReplayStats}; use super::kind::Translator; use super::local::LocalStatus; use super::quarantine; -use super::records::{self, unix_millis, EpochRecord, Record}; +use super::records::{self, EpochRecord, Record, unix_millis}; use super::snapshot::{self, META_ROOT, REMAKES}; -use super::Host; +use super::vote; use crate::document::replica::Epoch; use crate::document::store; use crate::swarm::sync::paths_related; @@ -78,8 +81,19 @@ async fn check<H: Host>(host: &H) { return catchup::check(host).await; } + vote::cast(host).await; + let all = host.read_doc(records::load_all).await; + content::prune( + host, + &all, + unix_millis(), + host.dormant_after().as_millis() as u64, + ); let local_record = EpochRecord::from(local); + if host.is_node_ready().await { + coordinator::resume(host, &local_record, &all); + } let committed: Vec<(String, Record)> = all .into_iter() @@ -91,9 +105,13 @@ async fn check<H: Host>(host: &H) { let (id, record) = match committed.as_slice() { // Nothing committed here; maybe peers moved on without us. [] => return catchup::check(host).await, + [(id, record)] if record.contested => { + error!(remake = %id, "Decisions on this remake disagree; not switching on my own"); + return catchup::check(host).await; + } [(id, record)] => (id.clone(), record.clone()), several => { - // The settle protocol prevents this; never guess which one is right. Peers + // Majority voting prevents this; never guess which one is right. Peers // that did switch can still be followed. let ids: Vec<&str> = several.iter().map(|(id, _)| id.as_str()).collect(); error!(remakes = ?ids, "Several remakes committed for this epoch; not switching on my own"); @@ -154,10 +172,16 @@ async fn switch<H: Host>( id: &str, record: &Record, ) -> Result<(ReplayStats, Option<String>), String> { - let (Some(expected), Some(request)) = (record.committed_epoch(), record.snapshot_request(id)) + let (Some(expected), Some(mut request)) = + (record.committed_epoch(), record.snapshot_request(id)) else { return Err("unreadable remake record".to_owned()); }; + // Voters fetched it already; a node that didn't vote fetches it now. Without it the + // switch fails, and the node rejoins (downloading the snapshot instead). + if let Some(reference) = &record.proposal.content { + request.content = Some(content::ensure(host, reference).await?); + } let translator = request .translator() .ok_or_else(|| format!("unknown remake kind {:?}", request.kind))?; |
