mirror of
https://github.com/jmcorgan/fips.git
synced 2026-08-10 08:37:02 +00:00
node: periodically re-broadcast TreeAnnounce on no-change in check_periodic_parent_reeval
Closes the eventually-consistent gap in spanning-tree state distribution. Every existing send_tree_announce_to_all call site gates on a local state-change event (parent switch, self-root promotion, ancestry change, peer promotion, parent loss). Once a partition latches — for example a parent-switch announce stranded in the brief cross-init handshake swap window, where the announce arrives on a session-index whose decrypt-worker entry has been unregistered — neither side's state changes again, so neither side ever re-broadcasts. The existing 60 s check_periodic_parent_reeval was a re-evaluation, not a re-broadcast: it short-circuited silently on no-change. Production-side healing depended on incidental link churn; lab harnesses with stable docker-bridge links had no equivalent path. Add a final else branch that fires send_tree_announce_to_all unconditionally on the no-change path, alongside the existing switch and self-promote arms. Receivers coalesce by sequence comparison (ParentDeclaration::is_fresher_than) and short-circuit at the `if !updated` gate in handle_tree_announce; same-sequence repeats drop silently with no cascade. The per-peer 500 ms rate-limiter is well below this 60 s cadence and does not suppress the heartbeat broadcast. The fix is a general protocol-robustness improvement: it addresses any in-flight TreeAnnounce loss class, not only the specific cross-init swap-window drop site. testing/static/scripts/rekey-test.sh BASELINE_CONVERGENCE_TIMEOUT 60 -> 65 so a partition healed by the periodic broadcast at T+60 lands inside the convergence window. wait_for_full_baseline early-exits on PASS, so successful reps see no extra wall-clock.
This commit is contained in:
@@ -495,6 +495,22 @@ impl Node {
|
||||
self.send_tree_announce_to_all().await;
|
||||
let all_peers: Vec<NodeAddr> = self.peers.keys().copied().collect();
|
||||
self.bloom_state.mark_all_updates_needed(all_peers);
|
||||
} else {
|
||||
// Periodic re-broadcast on no-change: makes TreeAnnounce
|
||||
// distribution eventually-consistent. Receivers coalesce
|
||||
// by sequence via ParentDeclaration::is_fresher_than and
|
||||
// short-circuit at the `if !updated` gate in
|
||||
// handle_tree_announce; the per-peer 500 ms rate-limiter
|
||||
// never blocks at this 60 s cadence. Closes the cross-init
|
||||
// in-flight loss recovery gap where the swap window can
|
||||
// strand one side's announce on a session-index the other
|
||||
// side cannot decrypt.
|
||||
trace!(
|
||||
seq = self.tree_state.my_declaration().sequence(),
|
||||
root = %self.tree_state.root(),
|
||||
"Periodic TreeAnnounce re-broadcast (no state change)"
|
||||
);
|
||||
self.send_tree_announce_to_all().await;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user