Take every down peer out of the table in one walk (closes #225)
check / check (push) Canceled after 0s

The prefix handler ran one PeerDown per peer, one after another, and
stopped taking messages at 20,000 held-back messages. Sessions going
down together queued up their walks, so a returning full-table peer
could reach that limit.

The table's PeersDown now takes several peers out of every prefix in
one walk. One walk runs at a time. The next one takes every peer whose
session went down meanwhile, so a message is held back for two walks at
most. Each walk takes about 2.6 seconds for each full-table peer it
takes out. The limit is now 150,000 messages, about 150 MiB: enough
when four full-table peers go down together. The status page shows how
many messages each handler holds back.

Model: opus-5-5
This commit is contained in:
2026-11-02 06:38:11 +00:00
committed by sneak
parent d66c59ef07
commit 9d37f98239
15 changed files with 436 additions and 123 deletions
@@ -7,11 +7,11 @@ import (
"time"
)
// TestPeerDownLetsReadsIn reads the table's counts again and again while a peer
// that carries more than two batches of prefixes goes down. PeerDown lets the
// lock go after every walkBatch prefixes, so a read must get in while the peer
// still carries some of its prefixes but no longer all.
func TestPeerDownLetsReadsIn(t *testing.T) {
// TestPeersDownLetsReadsIn reads the table's counts again and again while a
// peer that carries more than two batches of prefixes goes down. PeersDown lets
// the lock go after every walkBatch prefixes, so a read must get in while the
// peer still carries some of its prefixes but no longer all.
func TestPeersDownLetsReadsIn(t *testing.T) {
t.Parallel()
const (
@@ -32,8 +32,8 @@ func TestPeerDownLetsReadsIn(t *testing.T) {
addr = addr.Next()
}
// Hold a read lock until PeerDown waits for the lock, so that the first
// read below starts once PeerDown holds it. TryRLock fails once a writer
// Hold a read lock until PeersDown waits for the lock, so that the first
// read below starts once PeersDown holds it. TryRLock fails once a writer
// waits.
table.mu.RLock()
@@ -42,7 +42,7 @@ func TestPeerDownLetsReadsIn(t *testing.T) {
go func() {
defer close(peerDownDone)
table.PeerDown(peer)
table.PeersDown([]string{peer})
}()
for table.mu.TryRLock() {
@@ -60,7 +60,7 @@ func TestPeerDownLetsReadsIn(t *testing.T) {
select {
case <-peerDownDone:
t.Fatal("no read got in while PeerDown ran")
t.Fatal("no read got in while PeersDown ran")
default:
}
}