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 lets the lock go after every 10,000 prefixes. The handler runs it on a separate goroutine, one peer at a time. It holds back the down peer's later messages until that PeerDown returns. Then it 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
|
|
}
|