Record origin changes and show them on the prefix page (closes #161)
check / check (push) Successful in 4m6s
check / check (push) Successful in 4m6s
The routing table records each prefix and origin AS pair's origin changes: when it gains its first peer and when it loses its last. Each row holds the prefix, the origin AS, announced or withdrawn, the peer count and the time. The prefix handler writes them to a new origin_changes table, and the database maintainer prunes each prefix to its newest 500 rows and 72 hours. The prefix page lists them newest first, at most 5,000. Model: deepseek-v4-pro
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
package routetable
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"slices"
|
||||
"time"
|
||||
)
|
||||
|
||||
// maxWaitingOriginChanges is the most origin changes the table keeps waiting
|
||||
// for the prefix handler to write. A 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(),
|
||||
}
|
||||
|
||||
if len(t.originChanges) == maxWaitingOriginChanges {
|
||||
// The oldest change is dropped to make room.
|
||||
t.originChanges = t.originChanges[1:]
|
||||
t.originChangesDropped++
|
||||
}
|
||||
|
||||
t.originChanges = append(t.originChanges, change)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (t *Table) PutBackOriginChanges(changes []OriginChange) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
t.originChanges = append(changes, 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
|
||||
}
|
||||
Reference in New Issue
Block a user