check / check (push) Canceled after 0s
The table's PeerDown visits every prefix under the write lock, which takes over a second on a full table. The prefix handler called it inline, so its queue filled and the streamer dropped updates. PeerDown now holds the lock for 10,000 prefixes at a time, then lets the lock go for as long again. The handler runs PeerDown on a separate goroutine, one peer at a time. The handler holds back the down peer's later messages until that PeerDown returns. Then the handler applies them in order, one at a time, and takes new messages in between. It holds back at most 20,000 messages. Stop waits for all of this before its last flush. Model: opus-5-5
70 lines
1.4 KiB
Go
70 lines
1.4 KiB
Go
package routetable
|
|
|
|
import (
|
|
"net/netip"
|
|
"runtime"
|
|
"testing"
|
|
"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) {
|
|
t.Parallel()
|
|
|
|
const (
|
|
asn = 1000
|
|
peer = "192.0.2.1"
|
|
// prefixCount is more prefixes than two of walkBatch.
|
|
prefixCount = 2*walkBatch + 1
|
|
)
|
|
|
|
routeTime := time.Date(2026, 1, 2, 3, 4, 5, 0, time.UTC)
|
|
table := New(Params{})
|
|
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})
|
|
|
|
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
|
|
// waits.
|
|
table.mu.RLock()
|
|
|
|
peerDownDone := make(chan struct{})
|
|
|
|
go func() {
|
|
defer close(peerDownDone)
|
|
|
|
table.PeerDown(peer)
|
|
}()
|
|
|
|
for table.mu.TryRLock() {
|
|
table.mu.RUnlock()
|
|
runtime.Gosched()
|
|
}
|
|
|
|
table.mu.RUnlock()
|
|
|
|
for {
|
|
routes := table.Counts().IPv4Routes
|
|
if routes > 0 && routes < prefixCount {
|
|
break
|
|
}
|
|
|
|
select {
|
|
case <-peerDownDone:
|
|
t.Fatal("no read got in while PeerDown ran")
|
|
default:
|
|
}
|
|
}
|
|
|
|
<-peerDownDone
|
|
}
|