Compare commits
1
Commits
next
...
37d0fb13a9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37d0fb13a9 |
@@ -35,6 +35,12 @@ tested.
|
|||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
|
- 2026-11-02: working out the home page's cards no longer stops the prefix
|
||||||
|
handler. The walk over the routing table holds the table's lock for 10,000
|
||||||
|
prefixes at a time, then lets the lock go for as long again, as `PeerDown`
|
||||||
|
does. It used to take the lock back at once. The prefix handler then applied
|
||||||
|
about one update per 10,000 prefixes walked. During a walk, its queue could
|
||||||
|
fill, and the streamer then dropped updates for it (closes #226)
|
||||||
- 2026-11-02: like a failed WHOIS lookup, a lookup skipped because its RIR is
|
- 2026-11-02: like a failed WHOIS lookup, a lookup skipped because its RIR is
|
||||||
paused makes the fetcher wait longer before the next lookup. The longest wait
|
paused makes the fetcher wait longer before the next lookup. The longest wait
|
||||||
is one minute. The status page does not count a skip as a failure. A skip used
|
is one minute. The status page does not count a skip as a failure. A skip used
|
||||||
|
|||||||
@@ -79,8 +79,9 @@ type OriginPeers struct {
|
|||||||
|
|
||||||
// Overview works out the table's overview. It lists at most maxListed bogon
|
// 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:
|
// 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
|
// the first in prefix order. It visits every prefix in the table, in batches
|
||||||
// lock for walkBatch prefixes at a time.
|
// 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 {
|
func (t *Table) Overview(maxListed int) Overview {
|
||||||
var overview 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
|
// 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
|
// holds the read lock for walkBatch prefixes at a time. After each batch, it
|
||||||
// updates waiting for it get in. A prefix that is added or removed in such a
|
// lets the table's lock go for as long as it held the lock for the batch.
|
||||||
// gap may or may not be visited, as Go allows for a map that changes during a
|
// During a walk over many batches, updates can then take the lock about half
|
||||||
// range over it. visit must not keep origins once it returns.
|
// of the time. The walk may or may not visit a prefix that is added or removed
|
||||||
|
// while the lock is let go. Go allows this in a range over a map that changes.
|
||||||
|
// visit must not keep origins once it returns.
|
||||||
func (t *Table) walkPrefixes(visit func(prefix netip.Prefix, origins []*origin)) {
|
func (t *Table) walkPrefixes(visit func(prefix netip.Prefix, origins []*origin)) {
|
||||||
t.mu.RLock()
|
t.mu.RLock()
|
||||||
defer t.mu.RUnlock()
|
defer t.mu.RUnlock()
|
||||||
|
|
||||||
visited := 0
|
visited := 0
|
||||||
|
batchStart := time.Now()
|
||||||
|
|
||||||
for _, f := range []*family{&t.ipv4, &t.ipv6} {
|
for _, f := range []*family{&t.ipv4, &t.ipv6} {
|
||||||
for _, prefixes := range f.byLength {
|
for _, prefixes := range f.byLength {
|
||||||
@@ -127,8 +131,13 @@ func (t *Table) walkPrefixes(visit func(prefix netip.Prefix, origins []*origin))
|
|||||||
|
|
||||||
visited++
|
visited++
|
||||||
if visited%walkBatch == 0 {
|
if visited%walkBatch == 0 {
|
||||||
|
held := time.Since(batchStart)
|
||||||
|
|
||||||
t.mu.RUnlock()
|
t.mu.RUnlock()
|
||||||
|
time.Sleep(held)
|
||||||
t.mu.RLock()
|
t.mu.RLock()
|
||||||
|
|
||||||
|
batchStart = time.Now()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,3 +131,61 @@ func TestWalkPrefixesLetsUpdatesIn(t *testing.T) {
|
|||||||
"before its second batch")
|
"before its second batch")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestOverviewLetsUpdatesIn applies another peer's 1,000 announcements while
|
||||||
|
// Overview walks a table with more prefixes than five of the walk's batches.
|
||||||
|
// After each batch, the walk lets the lock go for as long as it held the lock.
|
||||||
|
// So all 1,000 announcements 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
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user