radicle: Announcer with replication factor of 0

Ensures that the Announcer will still announce to preferred seeds when the
replication factor is 0.
This commit is contained in:
Fintan Halpenny 2025-08-06 11:16:45 +01:00 committed by Lorenz Leutgeb
parent 4307eb35b5
commit 685c84e598
1 changed files with 61 additions and 2 deletions

View File

@ -182,14 +182,21 @@ impl Announcer {
}
fn is_target_reached(&self) -> Option<SuccessfulOutcome> {
// It should not be possible to construct a target that has no preferred
// seeds and set the target to 0
debug_assert!(self.target.has_preferred_seeds() || self.target.has_replication_factor());
let SuccessCounts { preferred, synced } = self.success_counts();
if !self.target.preferred_seeds.is_empty() && preferred >= self.target.preferred_seeds.len()
{
if self.target.has_preferred_seeds() && preferred >= self.target.preferred_seeds.len() {
Some(SuccessfulOutcome::PreferredNodes {
preferred: self.target.preferred_seeds.len(),
total_nodes_synced: synced,
})
} else {
// The only target to hit is preferred seeds
if !self.target.has_replication_factor() {
return None;
}
let replicas = self.target.replicas();
let min = replicas.lower_bound();
match replicas.upper_bound() {
@ -526,6 +533,16 @@ impl Target {
pub fn replicas(&self) -> &ReplicationFactor {
&self.replicas
}
/// Check if the target has preferred seeds
pub fn has_preferred_seeds(&self) -> bool {
!self.preferred_seeds.is_empty()
}
/// Check that lower bound of the replication is greater than `0`
pub fn has_replication_factor(&self) -> bool {
self.replicas.lower_bound() != 0
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@ -912,6 +929,48 @@ mod test {
}
}
#[test]
fn announcer_with_replication_factor_zero_and_preferred_seeds() {
let local = arbitrary::gen::<NodeId>(0);
let seeds = arbitrary::set::<NodeId>(5..=5);
let preferred_seeds = seeds.iter().take(2).copied().collect::<BTreeSet<_>>();
let unsynced = seeds.iter().skip(2).copied().collect::<BTreeSet<_>>();
// Zero replication factor but with preferred seeds should work
let config = AnnouncerConfig::public(
local,
// Zero replication factor
ReplicationFactor::must_reach(0),
preferred_seeds.clone(),
BTreeSet::new(),
unsynced,
);
let mut announcer = Announcer::new(config).unwrap();
// Should succeed immediately when we sync with all preferred seeds
for &node in &preferred_seeds {
let duration = time::Duration::from_secs(1);
match announcer.synced_with(node, duration) {
ControlFlow::Continue(_) => {} // Continue until all preferred are synced
ControlFlow::Break(success) => {
assert_eq!(
success.outcome(),
SuccessfulOutcome::PreferredNodes {
preferred: preferred_seeds.len(),
total_nodes_synced: preferred_seeds.len()
},
"Should succeed with preferred seeds even with zero replication factor"
);
return;
}
}
}
panic!("Should have succeeded with preferred seeds");
}
#[test]
fn cannot_construct_announcer() {
let local = arbitrary::gen::<NodeId>(0);