fix(peer): preserve monitor backoff per root

Track the last attempted configured root separately from the last successful snapshot. A root switch clears backoff once; repeated budget failures on that same root now retain exponential delay.

Test Plan:
- just test
- just clippy
- repeated failing-root backoff regression
- git diff --check
This commit is contained in:
2026-09-12 13:39:47 +02:00
parent f0a6d5f7ca
commit 07c51a10e9
@@ -132,9 +132,18 @@ impl PollBudget {
struct PollBackoff {
failures: u32,
retry_at: Option<Instant>,
observed_root: Option<PathBuf>,
}
impl PollBackoff {
fn observe_root(&mut self, root: &Path) {
if self.observed_root.as_deref() == Some(root) {
return;
}
self.observed_root = Some(root.to_path_buf());
self.record_success();
}
fn should_attempt(&self, now: Instant) -> bool {
self.retry_at.is_none_or(|retry_at| now >= retry_at)
}
@@ -264,14 +273,8 @@ async fn poll_local_game_changes(
previous: &mut Option<PollSnapshot>,
backoff: &mut PollBackoff,
) {
let configured_game_dir = ctx.game_dir.read().await;
if previous
.as_ref()
.is_some_and(|snapshot| snapshot.game_dir != *configured_game_dir)
{
backoff.record_success();
}
drop(configured_game_dir);
let configured_game_dir = ctx.game_dir.read().await.clone();
backoff.observe_root(&configured_game_dir);
let now = Instant::now();
if !backoff.should_attempt(now) {
@@ -733,6 +736,27 @@ mod tests {
assert_eq!(backoff.failures, 0);
}
#[test]
fn root_change_resets_backoff_only_once_until_a_snapshot_succeeds() {
let now = Instant::now();
let mut backoff = PollBackoff::default();
let first_root = Path::new("/first");
let failing_root = Path::new("/oversized");
backoff.observe_root(first_root);
backoff.record_failure(now);
backoff.observe_root(failing_root);
assert!(backoff.should_attempt(now));
let delay = backoff.record_failure(now);
backoff.observe_root(failing_root);
assert!(
!backoff.should_attempt(now),
"re-observing the same failing root must preserve its backoff"
);
assert!(backoff.should_attempt(now + delay));
}
#[test]
fn snapshot_diff_detects_game_change_and_disappearance() {
let temp = TempDir::new("lanspread-local-monitor-change");