summaryrefslogtreecommitdiffstats
path: root/src/remake/switch.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/remake/switch.rs')
-rw-r--r--src/remake/switch.rs32
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))?;