Add flap dampening to parent selection

Track parent switch frequency in a sliding window. When switches exceed
a configurable threshold (default 4 in 60s), impose an extended hold-down
period (default 120s) that prevents non-mandatory parent changes.

Mandatory switches (parent loss, root change, shouldn't-be-root) bypass
dampening. The flap counter resets when the window expires naturally.

Implements TASK-2026-0030 / IDEA-0013.
This commit is contained in:
Johnathan Corgan
2026-02-23 20:00:58 +00:00
parent 5d72e7cee1
commit 94c0e19951
5 changed files with 340 additions and 4 deletions
+10
View File
@@ -374,6 +374,11 @@ impl Node {
let mut tree_state = TreeState::new(node_addr);
tree_state.set_parent_hysteresis(config.node.tree.parent_hysteresis);
tree_state.set_hold_down(config.node.tree.hold_down_secs);
tree_state.set_flap_dampening(
config.node.tree.flap_threshold,
config.node.tree.flap_window_secs,
config.node.tree.flap_dampening_secs,
);
tree_state
.sign_declaration(&identity)
.expect("signing own declaration should never fail");
@@ -459,6 +464,11 @@ impl Node {
let mut tree_state = TreeState::new(node_addr);
tree_state.set_parent_hysteresis(config.node.tree.parent_hysteresis);
tree_state.set_hold_down(config.node.tree.hold_down_secs);
tree_state.set_flap_dampening(
config.node.tree.flap_threshold,
config.node.tree.flap_window_secs,
config.node.tree.flap_dampening_secs,
);
tree_state
.sign_declaration(&identity)
.expect("signing own declaration should never fail");
+8 -2
View File
@@ -207,7 +207,7 @@ impl Node {
.map(|d| d.as_secs())
.unwrap_or(0);
self.tree_state.set_parent(new_parent, new_seq, timestamp);
let flap_dampened = self.tree_state.set_parent(new_parent, new_seq, timestamp);
if let Err(e) = self.tree_state.sign_declaration(&self.identity) {
warn!(error = %e, "Failed to sign declaration after parent switch");
return;
@@ -222,6 +222,9 @@ impl Node {
depth = self.tree_state.my_coords().depth(),
"Parent switched, flushed coord cache, announcing to all peers"
);
if flap_dampened {
warn!("Flap dampening engaged: excessive parent switches detected");
}
self.send_tree_announce_to_all().await;
@@ -318,7 +321,7 @@ impl Node {
.map(|d| d.as_secs())
.unwrap_or(0);
self.tree_state.set_parent(new_parent, new_seq, timestamp);
let flap_dampened = self.tree_state.set_parent(new_parent, new_seq, timestamp);
if let Err(e) = self.tree_state.sign_declaration(&self.identity) {
warn!(error = %e, "Failed to sign declaration after periodic parent re-eval");
return;
@@ -334,6 +337,9 @@ impl Node {
trigger = "periodic",
"Parent switched via periodic cost re-evaluation"
);
if flap_dampened {
warn!("Flap dampening engaged: excessive parent switches detected");
}
self.send_tree_announce_to_all().await;