Files
routewatch/internal/routetable/originchanges.go
T
sneak 4581bc2cc4
check / check (push) Canceled after 57s
Fix the third review of the origin changes
The prune test gains a third prefix with more than 500 rows newer than 72
hours, and checks every newer row survives and every older one is deleted, in
a helper shared by the three prefixes. The prefix page test uses an origin AS
the routing table does not carry. README's limit list says the routing table
drops the oldest waiting origin changes past 100,000, and the prefix page
entry says the page shows at most the newest 5,000. The tests read 5,000 and
100,000 from named limits instead of bare numbers, so the routing table's
waiting cap is now exported for them.

Model: deepseek-v4-pro
2026-11-05 05:36:57 +00:00

104 lines
3.0 KiB
Go

package routetable
import (
"net/netip"
"slices"
"time"
)
// MaxWaitingOriginChanges is the most origin changes the table keeps waiting
// for the prefix handler to write. The oldest change past it is dropped and
// counted.
const MaxWaitingOriginChanges = 100000
// OriginChange is one change of a prefix and origin AS pair: the origin AS
// gained its first peer, or lost its last.
type OriginChange struct {
Prefix netip.Prefix
OriginASN int
// Announced says the origin AS gained its first peer. When false, it
// lost its last peer.
Announced bool
// PeerCount is how many peers carry the prefix just after the change,
// counting every origin AS.
PeerCount int
// Time is when RouteWatch applied the change.
Time time.Time
}
// recordOriginChange records that the pair of prefix and asn gained its first
// peer, when announced, or lost its last, when not. The caller holds the lock.
// peerCount is how many peers carry the prefix just after the change.
func (t *Table) recordOriginChange(
prefix netip.Prefix, asn int, announced bool, peerCount int,
) {
change := OriginChange{
Prefix: prefix,
OriginASN: asn,
Announced: announced,
PeerCount: peerCount,
Time: t.now(),
}
t.originChanges = append(t.originChanges, change)
t.trimOriginChanges()
}
// TakeOriginChanges returns at most limit of the origin changes that wait to
// be written, oldest first, and how many were dropped since the last call. It
// then forgets what it returned.
func (t *Table) TakeOriginChanges(limit int) ([]OriginChange, int) {
t.mu.Lock()
defer t.mu.Unlock()
n := min(limit, len(t.originChanges))
changes := slices.Clone(t.originChanges[:n])
t.originChanges = t.originChanges[n:]
dropped := t.originChangesDropped
t.originChangesDropped = 0
return changes, dropped
}
// PutBackOriginChanges records again what one TakeOriginChanges call returned,
// for a caller that could not write it. The next TakeOriginChanges returns
// those changes first, before the ones that arrived since. The oldest changes
// past the cap are dropped and counted.
func (t *Table) PutBackOriginChanges(changes []OriginChange) {
t.mu.Lock()
defer t.mu.Unlock()
t.originChanges = append(changes, t.originChanges...)
t.trimOriginChanges()
}
// trimOriginChanges drops the oldest origin changes past the cap and counts
// them. The caller holds the lock.
func (t *Table) trimOriginChanges() {
if extra := len(t.originChanges) - MaxWaitingOriginChanges; extra > 0 {
t.originChangesDropped += extra
t.originChanges = t.originChanges[extra:]
}
}
// OriginChangeCount returns how many origin changes wait for TakeOriginChanges.
func (t *Table) OriginChangeCount() int {
t.mu.RLock()
defer t.mu.RUnlock()
return len(t.originChanges)
}
// prefixPeerCount returns how many peers carry prefix, counting every origin
// AS. The caller holds the lock.
func (t *Table) prefixPeerCount(prefix netip.Prefix) int {
count := 0
for _, o := range t.origins(prefix) {
count += int(o.peers.Count())
}
return count
}