fix(lib): free a silent parent's slot and pace the moves we report

A candidate that accepted the dial and then said nothing kept its slot
for the session, and ten of those blocked every later batch; a link now
closes after a minute of silence. A parent that flaps its level made us
tell the server four messages per flap; moves are now told at most once
a second, the latest one winning. A parent that reports level zero is
its own root from then on, whatever it said before. The wait for a
searcher's control connection runs from the last message queued for it,
not the first. A successful login is pinned to reset the leaf.
This commit is contained in:
Michel de Graaf
2026-09-08 01:45:07 +02:00
parent 9d6a5aaa98
commit 85f26134ed
4 changed files with 111 additions and 14 deletions
+7 -5
View File
@@ -388,7 +388,8 @@ pub struct ClientContext {
/// Peer listen addresses learned from GetPeerAddress responses.
peer_addresses: HashMap<String, (String, u32)>,
/// Peer messages waiting for a control connection to that peer, and
/// since when: a peer that never answers must not keep them forever.
/// when the last was queued: a peer that never answers must not keep
/// them forever.
pending_peer_messages:
HashMap<String, (Instant, Vec<crate::message::Message>)>,
/// Uploads we have offered, keyed by our transfer token.
@@ -741,11 +742,12 @@ impl ClientContext {
username: &str,
message: crate::message::Message,
) {
self.pending_peer_messages
let entry = self
.pending_peer_messages
.entry(username.to_string())
.or_insert_with(|| (Instant::now(), Vec::new()))
.1
.push(message);
.or_insert_with(|| (Instant::now(), Vec::new()));
entry.0 = Instant::now();
entry.1.push(message);
}
/// Remove and return the messages queued for `username`.