Let the lock go for as long as it was held during Overview's walk (closes #226)
check / check (push) Canceled after 0s

The home page's cards are worked out once a minute by Overview, which
walks every prefix of the routing table under its read lock. After each
batch of 10,000 prefixes it let the lock go and took it back at once.
The prefix handler, waiting for the write lock, then got in only once
per batch. Its queue filled, and the streamer dropped updates for it.

The walk now lets the lock go for as long as it held it for the batch,
as PeerDown does. A test applies another peer's 1,000 announcements
while Overview walks a table of more than five batches.

Model: opus-5-5
This commit is contained in:
2026-11-02 05:51:12 +00:00
parent d66c59ef07
commit feecdb0ca3
3 changed files with 79 additions and 6 deletions
@@ -131,3 +131,61 @@ func TestWalkPrefixesLetsUpdatesIn(t *testing.T) {
"before its second batch")
}
}
// TestOverviewLetsUpdatesIn applies another peer's 1,000 announcements while
// Overview walks a table of more than five batches. After each batch, the walk
// lets the lock go for as long as it held it, so all of them must be applied
// before Overview returns. A walk that takes the lock back at once lets in
// about one update per batch.
func TestOverviewLetsUpdatesIn(t *testing.T) {
t.Parallel()
const (
asn = 1000
peer = "192.0.2.1"
otherPeer = "192.0.2.2"
// prefixCount is more prefixes than five of the walk's batches.
prefixCount = 5*walkBatch + 1
announcements = 1000
)
routeTime := time.Date(2026, 1, 2, 3, 4, 5, 0, time.UTC)
table := New(Params{})
prefixes := make([]netip.Prefix, 0, prefixCount)
addr := netip.MustParseAddr("1.0.0.0")
for range prefixCount {
prefix := netip.PrefixFrom(addr, addr.BitLen())
table.Announce(prefix, asn, Route{Peer: peer, Time: routeTime})
prefixes = append(prefixes, prefix)
addr = addr.Next()
}
overviewDone := make(chan struct{})
go func() {
defer close(overviewDone)
table.Overview(0)
}()
// Wait until Overview holds the read lock. TryLock fails while a reader
// holds it.
for table.mu.TryLock() {
table.mu.Unlock()
runtime.Gosched()
}
for _, prefix := range prefixes[:announcements] {
table.Announce(prefix, asn, Route{Peer: otherPeer, Time: routeTime})
}
select {
case <-overviewDone:
t.Fatal("Overview returned before another peer's 1,000 announcements " +
"were applied")
default:
}
<-overviewDone
}