diff options
| author | tv <tv@krebsco.de> | 2026-11-08 15:02:24 +0000 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-08 16:07:29 +0100 |
| commit | 9dcdc67253fbef058e292c68cf33faea55e0246d (patch) | |
| tree | 388cf009ed5547d6c97c5da7bb7b584cceb2a8dd /src/access/screen.rs | |
| parent | b9028758d85f8752f94b388c569847e5aa4ce21b (diff) | |
screen: a batch Automerge refuses names its changes
Review section 7: Automerge takes the changes judged in a batch all or
none, so one it won't take (a fork of an actor's changes, under a
policy without signatures) fails the whole batch, and every batch
carrying it. The log said who sent the message (`Failed to process sync
message`, `Delta download failed`, with the peer) but not what failed.
The error now names the changes it was given, the first eight, each
with its seq and the node it says made it, so whoever sends the
offending one shows in the log beside what it sent.
Diffstat (limited to 'src/access/screen.rs')
| -rw-r--r-- | src/access/screen.rs | 43 |
1 files changed, 41 insertions, 2 deletions
diff --git a/src/access/screen.rs b/src/access/screen.rs index aa16b42..ae61416 100644 --- a/src/access/screen.rs +++ b/src/access/screen.rs @@ -433,7 +433,7 @@ pub(crate) fn apply( if relevant(&paths[index]) || reaches_signed(doc, &paths[index]) { // What rules read changed: in before the next is judged. screened.applied += accepted.len(); - doc.apply_changes_log_patches(std::mem::take(&mut accepted), log)?; + apply_taken(doc, std::mem::take(&mut accepted), log)?; frontier.clear(); } else { frontier.insert(hash, seen_at); @@ -460,7 +460,7 @@ pub(crate) fn apply( } screened.applied += accepted.len(); if !accepted.is_empty() { - doc.apply_changes_log_patches(accepted, log)?; + apply_taken(doc, accepted, log)?; } for (hash, root) in refused { refusals.insert(hash, root); @@ -473,6 +473,45 @@ pub(crate) fn apply( Ok(screened) } +/// `accepted` applied to `doc`. Automerge takes them all or none: one it won't take (a +/// fork of an actor's changes, under a policy without signatures) fails the lot, so the +/// error names them (the first few), with the node each says made it, for the log of +/// whoever brought them. +fn apply_taken( + doc: &mut Document, + accepted: Vec<Change>, + log: &mut crate::document::PatchLog, +) -> Result<()> { + const NAMED: usize = 8; + let count = accepted.len(); + let mut named: Vec<String> = accepted + .iter() + .take(NAMED) + .map(|change| { + let author = change + .author() + .and_then(|author| { + std::str::from_utf8(author.as_bytes()) + .ok() + .map(str::to_owned) + }) + .unwrap_or_else(|| "its actor's node".to_owned()); + format!("{} (seq {} by {author})", change.hash(), change.seq()) + }) + .collect(); + if count > NAMED { + named.push(format!("{} more", count - NAMED)); + } + doc.apply_changes_log_patches(accepted, log) + .map_err(|error| { + anyhow::anyhow!( + "Automerge took none of the {count} changes judged: {error} ({})", + named.join(", ") + ) + })?; + Ok(()) +} + /// What `apply_bytes` did. #[derive(Debug, Default, Clone)] pub(crate) struct Outcome { |
