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:
+72
-19
@@ -88,6 +88,10 @@ const (
|
||||
ipVersionV6 = 6
|
||||
)
|
||||
|
||||
// maxOriginChangesOnPage is the most origin changes the prefix page shows. A
|
||||
// prefix with more shows the newest ones and says so.
|
||||
const maxOriginChangesOnPage = 5000
|
||||
|
||||
// HealthCheckResponse represents the health check response.
|
||||
type HealthCheckResponse struct {
|
||||
Status string `json:"status"`
|
||||
@@ -1534,27 +1538,24 @@ func (s *Server) handlePrefixDetail() http.HandlerFunc {
|
||||
|
||||
routes := prefixRoutes(parsed, origins)
|
||||
|
||||
// Prepare template data
|
||||
data := struct {
|
||||
Prefix string
|
||||
MaskLength int
|
||||
IPVersion int
|
||||
Routes []EnhancedRoute
|
||||
Origins []*ASNInfo
|
||||
PeerCount int
|
||||
OriginCount int
|
||||
CSPNonce string
|
||||
}{
|
||||
Prefix: parsed.String(),
|
||||
MaskLength: parsed.Bits(),
|
||||
IPVersion: ipVersionOf(parsed),
|
||||
Routes: routesWithASPathHandles(routes),
|
||||
Origins: originInfos,
|
||||
PeerCount: len(routes),
|
||||
OriginCount: len(originInfos),
|
||||
CSPNonce: cspNonce(r.Context()),
|
||||
changes, moreChanges, err := s.prefixOriginChanges(r.Context(), parsed)
|
||||
if deadlinePassed(r.Context()) {
|
||||
http.Error(w, requestTimeoutMessage, http.StatusGatewayTimeout)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to get the origin changes", "error", err)
|
||||
http.Error(w, internalErrorMessage, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Prepare template data
|
||||
data := newPrefixDetailData(parsed, routes, originInfos, changes,
|
||||
moreChanges, cspNonce(r.Context()))
|
||||
|
||||
// Check if context is still valid before writing response
|
||||
select {
|
||||
case <-r.Context().Done():
|
||||
@@ -1606,6 +1607,58 @@ func (s *Server) originASNs(
|
||||
return infos, nil
|
||||
}
|
||||
|
||||
// prefixOriginChanges returns prefix's origin changes for the page, newest
|
||||
// first, at most maxOriginChangesOnPage, and whether there are more.
|
||||
func (s *Server) prefixOriginChanges(
|
||||
ctx context.Context, prefix netip.Prefix,
|
||||
) ([]routetable.OriginChange, bool, error) {
|
||||
// One more is read to know whether there are more.
|
||||
changes, err := s.db.GetOriginChanges(ctx, prefix, maxOriginChangesOnPage+1)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
more := len(changes) > maxOriginChangesOnPage
|
||||
if more {
|
||||
changes = changes[:maxOriginChangesOnPage]
|
||||
}
|
||||
|
||||
return changes, more, nil
|
||||
}
|
||||
|
||||
// prefixDetailData is what the prefix detail page is rendered with.
|
||||
type prefixDetailData struct {
|
||||
Prefix string
|
||||
MaskLength int
|
||||
IPVersion int
|
||||
Routes []EnhancedRoute
|
||||
Origins []*ASNInfo
|
||||
PeerCount int
|
||||
OriginCount int
|
||||
OriginChanges []routetable.OriginChange
|
||||
MoreOriginChanges bool
|
||||
CSPNonce string
|
||||
}
|
||||
|
||||
// newPrefixDetailData builds the prefix detail page's data.
|
||||
func newPrefixDetailData(
|
||||
parsed netip.Prefix, routes []LiveRoute, originInfos []*ASNInfo,
|
||||
changes []routetable.OriginChange, moreChanges bool, nonce string,
|
||||
) prefixDetailData {
|
||||
return prefixDetailData{
|
||||
Prefix: parsed.String(),
|
||||
MaskLength: parsed.Bits(),
|
||||
IPVersion: ipVersionOf(parsed),
|
||||
Routes: routesWithASPathHandles(routes),
|
||||
Origins: originInfos,
|
||||
PeerCount: len(routes),
|
||||
OriginCount: len(originInfos),
|
||||
OriginChanges: changes,
|
||||
MoreOriginChanges: moreChanges,
|
||||
CSPNonce: nonce,
|
||||
}
|
||||
}
|
||||
|
||||
// prefixRoutes returns one route for each peer that carries prefix, by origin
|
||||
// AS in the order of origins. Each origin's kept routes come first, newest
|
||||
// first, with their AS path, next hop and time. Its other peers follow without
|
||||
|
||||
Reference in New Issue
Block a user