Remove RoutingTableHandler and snapshotter, use database for route stats
- Remove RoutingTableHandler as PrefixHandler maintains live_routes table - Update server to get route counts from database instead of in-memory routing table - Add GetLiveRouteCounts method to database for IPv4/IPv6 route counts - Use metrics tracker in PrefixHandler for route update rates - Remove snapshotter entirely as database contains all information - Update tests to work without routing table
This commit is contained in:
parent
cb1f4d9052
commit
d929f24f80
@ -522,3 +522,20 @@ func (d *Database) GetPrefixDistribution() (ipv4 []PrefixDistribution, ipv6 []Pr
|
|||||||
|
|
||||||
return ipv4, ipv6, nil
|
return ipv4, ipv6, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLiveRouteCounts returns the count of IPv4 and IPv6 routes
|
||||||
|
func (d *Database) GetLiveRouteCounts() (ipv4Count, ipv6Count int, err error) {
|
||||||
|
// Get IPv4 count
|
||||||
|
err = d.db.QueryRow("SELECT COUNT(*) FROM live_routes WHERE ip_version = 4").Scan(&ipv4Count)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, fmt.Errorf("failed to count IPv4 routes: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get IPv6 count
|
||||||
|
err = d.db.QueryRow("SELECT COUNT(*) FROM live_routes WHERE ip_version = 6").Scan(&ipv6Count)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, fmt.Errorf("failed to count IPv6 routes: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipv4Count, ipv6Count, nil
|
||||||
|
}
|
||||||
|
@ -41,6 +41,7 @@ type Store interface {
|
|||||||
UpsertLiveRoute(route *LiveRoute) error
|
UpsertLiveRoute(route *LiveRoute) error
|
||||||
DeleteLiveRoute(prefix string, originASN int, peerIP string) error
|
DeleteLiveRoute(prefix string, originASN int, peerIP string) error
|
||||||
GetPrefixDistribution() (ipv4 []PrefixDistribution, ipv6 []PrefixDistribution, err error)
|
GetPrefixDistribution() (ipv4 []PrefixDistribution, ipv6 []PrefixDistribution, err error)
|
||||||
|
GetLiveRouteCounts() (ipv4Count, ipv6Count int, err error)
|
||||||
|
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
Close() error
|
Close() error
|
||||||
|
@ -5,7 +5,6 @@ package routewatch
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -13,38 +12,28 @@ import (
|
|||||||
"git.eeqj.de/sneak/routewatch/internal/database"
|
"git.eeqj.de/sneak/routewatch/internal/database"
|
||||||
"git.eeqj.de/sneak/routewatch/internal/logger"
|
"git.eeqj.de/sneak/routewatch/internal/logger"
|
||||||
"git.eeqj.de/sneak/routewatch/internal/metrics"
|
"git.eeqj.de/sneak/routewatch/internal/metrics"
|
||||||
"git.eeqj.de/sneak/routewatch/internal/routingtable"
|
|
||||||
"git.eeqj.de/sneak/routewatch/internal/server"
|
"git.eeqj.de/sneak/routewatch/internal/server"
|
||||||
"git.eeqj.de/sneak/routewatch/internal/snapshotter"
|
|
||||||
"git.eeqj.de/sneak/routewatch/internal/streamer"
|
"git.eeqj.de/sneak/routewatch/internal/streamer"
|
||||||
|
|
||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// routingTableStatsInterval is how often we log routing table statistics
|
|
||||||
routingTableStatsInterval = 15 * time.Second
|
|
||||||
)
|
|
||||||
|
|
||||||
// Dependencies contains all dependencies for RouteWatch
|
// Dependencies contains all dependencies for RouteWatch
|
||||||
type Dependencies struct {
|
type Dependencies struct {
|
||||||
fx.In
|
fx.In
|
||||||
|
|
||||||
DB database.Store
|
DB database.Store
|
||||||
RoutingTable *routingtable.RoutingTable
|
Streamer *streamer.Streamer
|
||||||
Streamer *streamer.Streamer
|
Server *server.Server
|
||||||
Server *server.Server
|
Logger *logger.Logger
|
||||||
Logger *logger.Logger
|
Config *config.Config
|
||||||
Config *config.Config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RouteWatch represents the main application instance
|
// RouteWatch represents the main application instance
|
||||||
type RouteWatch struct {
|
type RouteWatch struct {
|
||||||
db database.Store
|
db database.Store
|
||||||
routingTable *routingtable.RoutingTable
|
|
||||||
streamer *streamer.Streamer
|
streamer *streamer.Streamer
|
||||||
server *server.Server
|
server *server.Server
|
||||||
snapshotter *snapshotter.Snapshotter
|
|
||||||
logger *logger.Logger
|
logger *logger.Logger
|
||||||
maxRuntime time.Duration
|
maxRuntime time.Duration
|
||||||
shutdown bool
|
shutdown bool
|
||||||
@ -56,38 +45,15 @@ type RouteWatch struct {
|
|||||||
peeringHandler *PeeringHandler
|
peeringHandler *PeeringHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
// isTruthy returns true if the value is considered truthy
|
|
||||||
// Empty string, "0", and "false" are considered falsy, everything else is truthy
|
|
||||||
func isTruthy(value string) bool {
|
|
||||||
return value != "" && value != "0" && value != "false"
|
|
||||||
}
|
|
||||||
|
|
||||||
// isSnapshotterEnabled checks if the snapshotter should be enabled based on environment variable
|
|
||||||
func isSnapshotterEnabled() bool {
|
|
||||||
return !isTruthy(os.Getenv("ROUTEWATCH_DISABLE_SNAPSHOTTER"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a new RouteWatch instance
|
// New creates a new RouteWatch instance
|
||||||
func New(deps Dependencies) *RouteWatch {
|
func New(deps Dependencies) *RouteWatch {
|
||||||
rw := &RouteWatch{
|
rw := &RouteWatch{
|
||||||
db: deps.DB,
|
db: deps.DB,
|
||||||
routingTable: deps.RoutingTable,
|
streamer: deps.Streamer,
|
||||||
streamer: deps.Streamer,
|
server: deps.Server,
|
||||||
server: deps.Server,
|
logger: deps.Logger,
|
||||||
logger: deps.Logger,
|
maxRuntime: deps.Config.MaxRuntime,
|
||||||
maxRuntime: deps.Config.MaxRuntime,
|
config: deps.Config,
|
||||||
config: deps.Config,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create snapshotter if enabled
|
|
||||||
if isSnapshotterEnabled() {
|
|
||||||
snap, err := snapshotter.New(deps.RoutingTable, deps.Config, deps.Logger)
|
|
||||||
if err != nil {
|
|
||||||
deps.Logger.Error("Failed to create snapshotter", "error", err)
|
|
||||||
// Continue without snapshotter
|
|
||||||
} else {
|
|
||||||
rw.snapshotter = snap
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rw
|
return rw
|
||||||
@ -131,17 +97,7 @@ func (rw *RouteWatch) Run(ctx context.Context) error {
|
|||||||
return fmt.Errorf("non-batched handlers not implemented")
|
return fmt.Errorf("non-batched handlers not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register routing table handler to maintain in-memory routing table
|
// No longer need routing table handler - PrefixHandler maintains live_routes table
|
||||||
rtHandler := NewRoutingTableHandler(rw.routingTable, rw.logger)
|
|
||||||
rw.streamer.RegisterHandler(rtHandler)
|
|
||||||
|
|
||||||
// Start periodic routing table stats logging
|
|
||||||
go rw.logRoutingTableStats(ctx)
|
|
||||||
|
|
||||||
// Start snapshotter if available
|
|
||||||
if rw.snapshotter != nil {
|
|
||||||
rw.snapshotter.Start(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start streaming
|
// Start streaming
|
||||||
if err := rw.streamer.Start(); err != nil {
|
if err := rw.streamer.Start(); err != nil {
|
||||||
@ -187,9 +143,6 @@ func (rw *RouteWatch) Shutdown() {
|
|||||||
// Stop services
|
// Stop services
|
||||||
rw.streamer.Stop()
|
rw.streamer.Stop()
|
||||||
|
|
||||||
// Stop routing table expiration
|
|
||||||
rw.routingTable.Stop()
|
|
||||||
|
|
||||||
// Stop HTTP server with a timeout
|
// Stop HTTP server with a timeout
|
||||||
const serverStopTimeout = 5 * time.Second
|
const serverStopTimeout = 5 * time.Second
|
||||||
stopCtx, cancel := context.WithTimeout(context.Background(), serverStopTimeout)
|
stopCtx, cancel := context.WithTimeout(context.Background(), serverStopTimeout)
|
||||||
@ -208,43 +161,6 @@ func (rw *RouteWatch) Shutdown() {
|
|||||||
"duration", time.Since(metrics.ConnectedSince),
|
"duration", time.Since(metrics.ConnectedSince),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Take final snapshot before shutdown if snapshotter is available
|
|
||||||
if rw.snapshotter != nil {
|
|
||||||
rw.logger.Info("Taking final snapshot before shutdown")
|
|
||||||
if err := rw.snapshotter.Shutdown(); err != nil {
|
|
||||||
rw.logger.Error("Failed to shutdown snapshotter", "error", err)
|
|
||||||
} else {
|
|
||||||
rw.logger.Info("Final snapshot completed")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
rw.logger.Info("No snapshotter available")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// logRoutingTableStats periodically logs routing table statistics
|
|
||||||
func (rw *RouteWatch) logRoutingTableStats(ctx context.Context) {
|
|
||||||
// Log stats periodically
|
|
||||||
ticker := time.NewTicker(routingTableStatsInterval)
|
|
||||||
defer ticker.Stop()
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
case <-ticker.C:
|
|
||||||
stats := rw.routingTable.GetDetailedStats()
|
|
||||||
rw.logger.Info("Routing table statistics",
|
|
||||||
"ipv4_routes", stats.IPv4Routes,
|
|
||||||
"ipv6_routes", stats.IPv6Routes,
|
|
||||||
"ipv4_updates_per_sec", fmt.Sprintf("%.2f", stats.IPv4UpdatesRate),
|
|
||||||
"ipv6_updates_per_sec", fmt.Sprintf("%.2f", stats.IPv6UpdatesRate),
|
|
||||||
"total_routes", stats.TotalRoutes,
|
|
||||||
"unique_prefixes", stats.UniquePrefixes,
|
|
||||||
"unique_origins", stats.UniqueOrigins,
|
|
||||||
"unique_peers", stats.UniquePeers,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getModule provides all fx dependencies
|
// getModule provides all fx dependencies
|
||||||
@ -258,7 +174,6 @@ func getModule() fx.Option {
|
|||||||
database.New,
|
database.New,
|
||||||
fx.As(new(database.Store)),
|
fx.As(new(database.Store)),
|
||||||
),
|
),
|
||||||
routingtable.New,
|
|
||||||
streamer.New,
|
streamer.New,
|
||||||
server.New,
|
server.New,
|
||||||
New,
|
New,
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
"git.eeqj.de/sneak/routewatch/internal/database"
|
"git.eeqj.de/sneak/routewatch/internal/database"
|
||||||
"git.eeqj.de/sneak/routewatch/internal/logger"
|
"git.eeqj.de/sneak/routewatch/internal/logger"
|
||||||
"git.eeqj.de/sneak/routewatch/internal/metrics"
|
"git.eeqj.de/sneak/routewatch/internal/metrics"
|
||||||
"git.eeqj.de/sneak/routewatch/internal/routingtable"
|
|
||||||
"git.eeqj.de/sneak/routewatch/internal/server"
|
"git.eeqj.de/sneak/routewatch/internal/server"
|
||||||
"git.eeqj.de/sneak/routewatch/internal/streamer"
|
"git.eeqj.de/sneak/routewatch/internal/streamer"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@ -181,9 +180,13 @@ func (m *mockStore) GetPrefixDistribution() (ipv4 []database.PrefixDistribution,
|
|||||||
return nil, nil, nil
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLiveRouteCounts mock implementation
|
||||||
|
func (m *mockStore) GetLiveRouteCounts() (ipv4Count, ipv6Count int, err error) {
|
||||||
|
// Return mock counts
|
||||||
|
return m.RouteCount / 2, m.RouteCount / 2, nil
|
||||||
|
}
|
||||||
|
|
||||||
func TestRouteWatchLiveFeed(t *testing.T) {
|
func TestRouteWatchLiveFeed(t *testing.T) {
|
||||||
// Disable snapshotter for tests
|
|
||||||
t.Setenv("ROUTEWATCH_DISABLE_SNAPSHOTTER", "1")
|
|
||||||
|
|
||||||
// Create mock database
|
// Create mock database
|
||||||
mockDB := newMockStore()
|
mockDB := newMockStore()
|
||||||
@ -204,20 +207,16 @@ func TestRouteWatchLiveFeed(t *testing.T) {
|
|||||||
EnableBatchedDatabaseWrites: true,
|
EnableBatchedDatabaseWrites: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create routing table
|
|
||||||
rt := routingtable.New(cfg, logger)
|
|
||||||
|
|
||||||
// Create server
|
// Create server
|
||||||
srv := server.New(mockDB, rt, s, logger)
|
srv := server.New(mockDB, s, logger)
|
||||||
|
|
||||||
// Create RouteWatch with 5 second limit
|
// Create RouteWatch with 5 second limit
|
||||||
deps := Dependencies{
|
deps := Dependencies{
|
||||||
DB: mockDB,
|
DB: mockDB,
|
||||||
RoutingTable: rt,
|
Streamer: s,
|
||||||
Streamer: s,
|
Server: srv,
|
||||||
Server: srv,
|
Logger: logger,
|
||||||
Logger: logger,
|
Config: cfg,
|
||||||
Config: cfg,
|
|
||||||
}
|
}
|
||||||
rw := New(deps)
|
rw := New(deps)
|
||||||
|
|
||||||
|
@ -1,131 +0,0 @@
|
|||||||
package routewatch
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"git.eeqj.de/sneak/routewatch/internal/logger"
|
|
||||||
"git.eeqj.de/sneak/routewatch/internal/ristypes"
|
|
||||||
"git.eeqj.de/sneak/routewatch/internal/routingtable"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// routingTableHandlerQueueSize is the queue capacity for in-memory routing table operations
|
|
||||||
routingTableHandlerQueueSize = 100000
|
|
||||||
)
|
|
||||||
|
|
||||||
// RoutingTableHandler handles BGP messages and updates the in-memory routing table
|
|
||||||
type RoutingTableHandler struct {
|
|
||||||
rt *routingtable.RoutingTable
|
|
||||||
logger *logger.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRoutingTableHandler creates a new routing table handler
|
|
||||||
func NewRoutingTableHandler(rt *routingtable.RoutingTable, logger *logger.Logger) *RoutingTableHandler {
|
|
||||||
return &RoutingTableHandler{
|
|
||||||
rt: rt,
|
|
||||||
logger: logger,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WantsMessage returns true if this handler wants to process messages of the given type
|
|
||||||
func (h *RoutingTableHandler) WantsMessage(messageType string) bool {
|
|
||||||
// We only care about UPDATE messages for the routing table
|
|
||||||
return messageType == "UPDATE"
|
|
||||||
}
|
|
||||||
|
|
||||||
// QueueCapacity returns the desired queue capacity for this handler
|
|
||||||
func (h *RoutingTableHandler) QueueCapacity() int {
|
|
||||||
// In-memory operations are very fast, so use a large queue
|
|
||||||
return routingTableHandlerQueueSize
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleMessage processes a RIS message and updates the routing table
|
|
||||||
func (h *RoutingTableHandler) HandleMessage(msg *ristypes.RISMessage) {
|
|
||||||
// Use the pre-parsed timestamp
|
|
||||||
timestamp := msg.ParsedTimestamp
|
|
||||||
|
|
||||||
// Parse peer ASN
|
|
||||||
peerASN, err := strconv.Atoi(msg.PeerASN)
|
|
||||||
if err != nil {
|
|
||||||
h.logger.Error("Failed to parse peer ASN", "peer_asn", msg.PeerASN, "error", err)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get origin ASN from path (last element)
|
|
||||||
var originASN int
|
|
||||||
if len(msg.Path) > 0 {
|
|
||||||
originASN = msg.Path[len(msg.Path)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process announcements
|
|
||||||
for _, announcement := range msg.Announcements {
|
|
||||||
for _, prefix := range announcement.Prefixes {
|
|
||||||
// Generate deterministic UUIDs based on the prefix and origin ASN
|
|
||||||
// This ensures consistency across restarts
|
|
||||||
prefixID := uuid.NewSHA1(uuid.NameSpaceURL, []byte(prefix))
|
|
||||||
originASNID := uuid.NewSHA1(uuid.NameSpaceOID, []byte(strconv.Itoa(originASN)))
|
|
||||||
|
|
||||||
// Create route for the routing table
|
|
||||||
route := &routingtable.Route{
|
|
||||||
PrefixID: prefixID,
|
|
||||||
Prefix: prefix,
|
|
||||||
OriginASNID: originASNID,
|
|
||||||
OriginASN: originASN,
|
|
||||||
PeerASN: peerASN,
|
|
||||||
ASPath: msg.Path,
|
|
||||||
NextHop: announcement.NextHop,
|
|
||||||
AnnouncedAt: timestamp,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add route to routing table
|
|
||||||
h.rt.AddRoute(route)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process withdrawals
|
|
||||||
for _, prefix := range msg.Withdrawals {
|
|
||||||
// Generate deterministic UUID for the prefix
|
|
||||||
prefixID := uuid.NewSHA1(uuid.NameSpaceURL, []byte(prefix))
|
|
||||||
|
|
||||||
// Withdraw all routes for this prefix from this peer
|
|
||||||
h.rt.WithdrawRoutesByPrefixAndPeer(prefixID, peerASN)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRoutingTableStats returns statistics about the routing table
|
|
||||||
func (h *RoutingTableHandler) GetRoutingTableStats() map[string]int {
|
|
||||||
return h.rt.Stats()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetActiveRouteCount returns the number of active routes
|
|
||||||
func (h *RoutingTableHandler) GetActiveRouteCount() int {
|
|
||||||
return h.rt.Size()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRoutesByPrefix returns all routes for a specific prefix
|
|
||||||
func (h *RoutingTableHandler) GetRoutesByPrefix(prefixID uuid.UUID) []*routingtable.Route {
|
|
||||||
return h.rt.GetRoutesByPrefix(prefixID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRoutesByOriginASN returns all routes originated by a specific ASN
|
|
||||||
func (h *RoutingTableHandler) GetRoutesByOriginASN(originASNID uuid.UUID) []*routingtable.Route {
|
|
||||||
return h.rt.GetRoutesByOriginASN(originASNID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRoutesByPeerASN returns all routes received from a specific peer ASN
|
|
||||||
func (h *RoutingTableHandler) GetRoutesByPeerASN(peerASN int) []*routingtable.Route {
|
|
||||||
return h.rt.GetRoutesByPeerASN(peerASN)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAllRoutes returns all active routes
|
|
||||||
func (h *RoutingTableHandler) GetAllRoutes() []*routingtable.Route {
|
|
||||||
return h.rt.GetAllRoutes()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClearRoutingTable clears all routes from the routing table
|
|
||||||
func (h *RoutingTableHandler) ClearRoutingTable() {
|
|
||||||
h.rt.Clear()
|
|
||||||
h.logger.Info("Cleared routing table")
|
|
||||||
}
|
|
@ -10,7 +10,6 @@ import (
|
|||||||
|
|
||||||
"git.eeqj.de/sneak/routewatch/internal/database"
|
"git.eeqj.de/sneak/routewatch/internal/database"
|
||||||
"git.eeqj.de/sneak/routewatch/internal/logger"
|
"git.eeqj.de/sneak/routewatch/internal/logger"
|
||||||
"git.eeqj.de/sneak/routewatch/internal/routingtable"
|
|
||||||
"git.eeqj.de/sneak/routewatch/internal/streamer"
|
"git.eeqj.de/sneak/routewatch/internal/streamer"
|
||||||
"git.eeqj.de/sneak/routewatch/internal/templates"
|
"git.eeqj.de/sneak/routewatch/internal/templates"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
@ -19,21 +18,19 @@ import (
|
|||||||
|
|
||||||
// Server provides HTTP endpoints for status monitoring
|
// Server provides HTTP endpoints for status monitoring
|
||||||
type Server struct {
|
type Server struct {
|
||||||
router *chi.Mux
|
router *chi.Mux
|
||||||
db database.Store
|
db database.Store
|
||||||
routingTable *routingtable.RoutingTable
|
streamer *streamer.Streamer
|
||||||
streamer *streamer.Streamer
|
logger *logger.Logger
|
||||||
logger *logger.Logger
|
srv *http.Server
|
||||||
srv *http.Server
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new HTTP server
|
// New creates a new HTTP server
|
||||||
func New(db database.Store, rt *routingtable.RoutingTable, streamer *streamer.Streamer, logger *logger.Logger) *Server {
|
func New(db database.Store, streamer *streamer.Streamer, logger *logger.Logger) *Server {
|
||||||
s := &Server{
|
s := &Server{
|
||||||
db: db,
|
db: db,
|
||||||
routingTable: rt,
|
streamer: streamer,
|
||||||
streamer: streamer,
|
logger: logger,
|
||||||
logger: logger,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s.setupRoutes()
|
s.setupRoutes()
|
||||||
@ -196,8 +193,15 @@ func (s *Server) handleStatusJSON() http.HandlerFunc {
|
|||||||
|
|
||||||
const bitsPerMegabit = 1000000.0
|
const bitsPerMegabit = 1000000.0
|
||||||
|
|
||||||
// Get detailed routing table stats
|
// Get route counts from database
|
||||||
rtStats := s.routingTable.GetDetailedStats()
|
ipv4Routes, ipv6Routes, err := s.db.GetLiveRouteCounts()
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Warn("Failed to get live route counts", "error", err)
|
||||||
|
// Continue with zero counts
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get route update metrics
|
||||||
|
routeMetrics := s.streamer.GetMetricsTracker().GetRouteMetrics()
|
||||||
|
|
||||||
stats := Stats{
|
stats := Stats{
|
||||||
Uptime: uptime,
|
Uptime: uptime,
|
||||||
@ -213,10 +217,10 @@ func (s *Server) handleStatusJSON() http.HandlerFunc {
|
|||||||
Peerings: dbStats.Peerings,
|
Peerings: dbStats.Peerings,
|
||||||
DatabaseSizeBytes: dbStats.FileSizeBytes,
|
DatabaseSizeBytes: dbStats.FileSizeBytes,
|
||||||
LiveRoutes: dbStats.LiveRoutes,
|
LiveRoutes: dbStats.LiveRoutes,
|
||||||
IPv4Routes: rtStats.IPv4Routes,
|
IPv4Routes: ipv4Routes,
|
||||||
IPv6Routes: rtStats.IPv6Routes,
|
IPv6Routes: ipv6Routes,
|
||||||
IPv4UpdatesPerSec: rtStats.IPv4UpdatesRate,
|
IPv4UpdatesPerSec: routeMetrics.IPv4UpdatesPerSec,
|
||||||
IPv6UpdatesPerSec: rtStats.IPv6UpdatesRate,
|
IPv6UpdatesPerSec: routeMetrics.IPv6UpdatesPerSec,
|
||||||
IPv4PrefixDistribution: dbStats.IPv4PrefixDistribution,
|
IPv4PrefixDistribution: dbStats.IPv4PrefixDistribution,
|
||||||
IPv6PrefixDistribution: dbStats.IPv6PrefixDistribution,
|
IPv6PrefixDistribution: dbStats.IPv6PrefixDistribution,
|
||||||
}
|
}
|
||||||
@ -324,8 +328,15 @@ func (s *Server) handleStats() http.HandlerFunc {
|
|||||||
|
|
||||||
const bitsPerMegabit = 1000000.0
|
const bitsPerMegabit = 1000000.0
|
||||||
|
|
||||||
// Get detailed routing table stats
|
// Get route counts from database
|
||||||
rtStats := s.routingTable.GetDetailedStats()
|
ipv4Routes, ipv6Routes, err := s.db.GetLiveRouteCounts()
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Warn("Failed to get live route counts", "error", err)
|
||||||
|
// Continue with zero counts
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get route update metrics
|
||||||
|
routeMetrics := s.streamer.GetMetricsTracker().GetRouteMetrics()
|
||||||
|
|
||||||
// Get handler stats
|
// Get handler stats
|
||||||
handlerStats := s.streamer.GetHandlerStats()
|
handlerStats := s.streamer.GetHandlerStats()
|
||||||
@ -358,10 +369,10 @@ func (s *Server) handleStats() http.HandlerFunc {
|
|||||||
Peerings: dbStats.Peerings,
|
Peerings: dbStats.Peerings,
|
||||||
DatabaseSizeBytes: dbStats.FileSizeBytes,
|
DatabaseSizeBytes: dbStats.FileSizeBytes,
|
||||||
LiveRoutes: dbStats.LiveRoutes,
|
LiveRoutes: dbStats.LiveRoutes,
|
||||||
IPv4Routes: rtStats.IPv4Routes,
|
IPv4Routes: ipv4Routes,
|
||||||
IPv6Routes: rtStats.IPv6Routes,
|
IPv6Routes: ipv6Routes,
|
||||||
IPv4UpdatesPerSec: rtStats.IPv4UpdatesRate,
|
IPv4UpdatesPerSec: routeMetrics.IPv4UpdatesPerSec,
|
||||||
IPv6UpdatesPerSec: rtStats.IPv6UpdatesRate,
|
IPv6UpdatesPerSec: routeMetrics.IPv6UpdatesPerSec,
|
||||||
HandlerStats: handlerStatsInfo,
|
HandlerStats: handlerStatsInfo,
|
||||||
IPv4PrefixDistribution: dbStats.IPv4PrefixDistribution,
|
IPv4PrefixDistribution: dbStats.IPv4PrefixDistribution,
|
||||||
IPv6PrefixDistribution: dbStats.IPv6PrefixDistribution,
|
IPv6PrefixDistribution: dbStats.IPv6PrefixDistribution,
|
||||||
|
Loading…
Reference in New Issue
Block a user