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
+15 -6
View File
@@ -79,8 +79,9 @@ type OriginPeers struct {
// Overview works out the table's overview. It lists at most maxListed bogon
// announcements and at most maxListed prefixes with more than one origin AS:
// the first in prefix order. It walks over every prefix, but holds the read
// lock for walkBatch prefixes at a time.
// the first in prefix order. It visits every prefix in the table, in batches
// of walkBatch. After each batch, it lets the table's lock go for as long as
// it held the lock for the batch.
func (t *Table) Overview(maxListed int) Overview {
var overview Overview
@@ -110,15 +111,18 @@ func (t *Table) Overview(maxListed int) Overview {
}
// walkPrefixes calls visit with every prefix of the table and its origins. It
// holds the read lock, but lets it go after every walkBatch prefixes, so the
// updates waiting for it get in. A prefix that is added or removed in such a
// gap may or may not be visited, as Go allows for a map that changes during a
// range over it. visit must not keep origins once it returns.
// holds the read lock for walkBatch prefixes at a time. After each batch, it
// lets the table's lock go for as long as it held the lock for the batch.
// During a walk over many batches, updates can then take the lock about half
// of the time. A prefix that is added or removed while the lock is let go may
// or may not be visited, as Go allows for a map that changes during a range
// over it. visit must not keep origins once it returns.
func (t *Table) walkPrefixes(visit func(prefix netip.Prefix, origins []*origin)) {
t.mu.RLock()
defer t.mu.RUnlock()
visited := 0
batchStart := time.Now()
for _, f := range []*family{&t.ipv4, &t.ipv6} {
for _, prefixes := range f.byLength {
@@ -127,8 +131,13 @@ func (t *Table) walkPrefixes(visit func(prefix netip.Prefix, origins []*origin))
visited++
if visited%walkBatch == 0 {
held := time.Since(batchStart)
t.mu.RUnlock()
time.Sleep(held)
t.mu.RLock()
batchStart = time.Now()
}
}
}