fix load time when cache is not ready with openbgpd

This commit is contained in:
Annika Hannig 2022-01-10 17:02:44 +01:00
parent 4b180a7b34
commit cac7d44f87
10 changed files with 143 additions and 18 deletions

View File

@ -1 +1 @@
5.0.1
5.1.0

View File

@ -24,7 +24,10 @@ func (s *Server) apiNeighborsList(
var neighborsResponse *api.NeighborsResponse
// Try to fetch neighbors from store, only fall back
// to RS query if store is not ready yet
// to RS query if store is not ready yet.
// The stored neighbors response includes details like
// the number of filtered routes which might be lacking
// from the summary.
if s.neighborsStore.IsInitialized(rsID) {
status, err := s.neighborsStore.GetStatus(rsID)
neighbors, err := s.neighborsStore.GetNeighborsAt(ctx, rsID)
@ -51,7 +54,7 @@ func (s *Server) apiNeighborsList(
if source == nil {
return nil, ErrSourceNotFound
}
neighborsResponse, err = source.Neighbors()
neighborsResponse, err = source.NeighborsSummary()
if err != nil {
s.logSourceError("neighbors", rsID, err)
return nil, err

View File

@ -395,6 +395,11 @@ func (src *MultiTableBirdwatcher) Neighbors() (*api.NeighborsResponse, error) {
return response, nil // dereference for now
}
// NeighborsSummary is for now using Neighbors
func (src *MultiTableBirdwatcher) NeighborsSummary() (*api.NeighborsResponse, error) {
return src.Neighbors()
}
// Routes gets filtered and exported route
// from the birdwatcher backend.
func (src *MultiTableBirdwatcher) Routes(

View File

@ -182,10 +182,14 @@ func (src *SingleTableBirdwatcher) Neighbors() (*api.NeighborsResponse, error) {
// Cache result
src.neighborsCache.Set(response)
return response, nil // dereference for now
}
// NeighborsSummary is for now an alias of Neighbors
func (src *SingleTableBirdwatcher) NeighborsSummary() (*api.NeighborsResponse, error) {
return src.Neighbors()
}
// Routes gets filtered and exported routes
func (src *SingleTableBirdwatcher) Routes(
neighborID string,

View File

@ -202,6 +202,11 @@ func (gobgp *GoBGP) Neighbors() (*api.NeighborsResponse, error) {
return &response, nil
}
// NeighborsSummary is an alias of Neighbors for now
func (gobgp *GoBGP) NeighborsSummary() (*api.NeighborsResponse, error) {
return gobgp.Neighbors()
}
// Routes retrieves filtered and exported routes
func (gobgp *GoBGP) Routes(neighborID string) (*api.RoutesResponse, error) {
neigh, err := gobgp.lookupNeighbor(neighborID)

View File

@ -23,7 +23,8 @@ type BgplgdSource struct {
cfg *Config
// Store the neighbor responses from the server here
neighborsCache *caches.NeighborsCache
neighborsCache *caches.NeighborsCache
neighborsSummaryCache *caches.NeighborsCache
// Store the routes responses from the server
// here identified by neighborID
@ -38,16 +39,18 @@ func NewBgplgdSource(cfg *Config) *BgplgdSource {
// Initialize caches
nc := caches.NewNeighborsCache(cacheDisabled)
nsc := caches.NewNeighborsCache(cacheDisabled)
rc := caches.NewRoutesCache(cacheDisabled, cfg.RoutesCacheSize)
rrc := caches.NewRoutesCache(cacheDisabled, cfg.RoutesCacheSize)
rfc := caches.NewRoutesCache(cacheDisabled, cfg.RoutesCacheSize)
return &BgplgdSource{
cfg: cfg,
neighborsCache: nc,
routesCache: rc,
routesReceivedCache: rrc,
routesFilteredCache: rfc,
cfg: cfg,
neighborsCache: nc,
neighborsSummaryCache: nsc,
routesCache: rc,
routesReceivedCache: rrc,
routesFilteredCache: rfc,
}
}
@ -172,6 +175,51 @@ func (src *BgplgdSource) Neighbors() (*api.NeighborsResponse, error) {
return response, nil
}
// NeighborsSummary retrievs list of neighbors, which
// might lack details like with number of rejected routes.
// It is much faster though.
func (src *BgplgdSource) NeighborsSummary() (*api.NeighborsResponse, error) {
// Query cache and see if we have a hit
response := src.neighborsSummaryCache.Get()
if response != nil {
response.Meta.ResultFromCache = true
return response, nil
}
// Make API request and read response
req, err := src.ShowNeighborsRequest(context.Background())
if err != nil {
return nil, err
}
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
body, err := decoders.ReadJSONResponse(res)
if err != nil {
return nil, err
}
nb, err := decodeNeighbors(body)
if err != nil {
return nil, err
}
// Set route server id (sourceID) for all neighbors and
// calculate the filtered routes.
for _, n := range nb {
n.RouteServerID = src.cfg.ID
}
response = &api.NeighborsResponse{
Response: api.Response{
Meta: src.makeResponseMeta(),
},
Neighbors: nb,
}
src.neighborsSummaryCache.Set(response)
return response, nil
}
// NeighborsStatus retrives the status summary
// for all neightbors
func (src *BgplgdSource) NeighborsStatus() (*api.NeighborsStatusResponse, error) {

View File

@ -28,7 +28,8 @@ type StateServerSource struct {
cfg *Config
// Store the neighbor responses from the server here
neighborsCache *caches.NeighborsCache
neighborsCache *caches.NeighborsCache
neighborsSummaryCache *caches.NeighborsCache
// Store the routes responses from the server
// here identified by neighborID
@ -44,16 +45,18 @@ func NewStateServerSource(cfg *Config) *StateServerSource {
// Initialize caches
nc := caches.NewNeighborsCache(cacheDisabled)
nsc := caches.NewNeighborsCache(cacheDisabled)
rc := caches.NewRoutesCache(cacheDisabled, cfg.RoutesCacheSize)
rrc := caches.NewRoutesCache(cacheDisabled, cfg.RoutesCacheSize)
rfc := caches.NewRoutesCache(cacheDisabled, cfg.RoutesCacheSize)
return &StateServerSource{
cfg: cfg,
neighborsCache: nc,
routesCache: rc,
routesReceivedCache: rrc,
routesFilteredCache: rfc,
cfg: cfg,
neighborsCache: nc,
neighborsSummaryCache: nsc,
routesCache: rc,
routesReceivedCache: rrc,
routesFilteredCache: rfc,
}
}
@ -195,6 +198,49 @@ func (src *StateServerSource) Neighbors() (*api.NeighborsResponse, error) {
return response, nil
}
// NeighborsSummary retrieves the neighbors without additional
// information but as quickly as possible. The result will lack
// a reject count.
func (src *StateServerSource) NeighborsSummary() (*api.NeighborsResponse, error) {
response := src.neighborsSummaryCache.Get()
if response != nil {
response.Meta.ResultFromCache = true
return response, nil
}
// Make API request and read response
req, err := src.ShowNeighborsRequest(context.Background())
if err != nil {
return nil, err
}
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
body, err := decoders.ReadJSONResponse(res)
if err != nil {
return nil, err
}
nb, err := decodeNeighbors(body)
if err != nil {
return nil, err
}
// Set route server id (sourceID) for all neighbors
for _, n := range nb {
n.RouteServerID = src.cfg.ID
}
response = &api.NeighborsResponse{
Response: api.Response{
Meta: src.makeResponseMeta(),
},
Neighbors: nb,
}
src.neighborsSummaryCache.Set(response)
return response, nil
}
// NeighborsStatus retrives the status summary
// for all neightbors
func (src *StateServerSource) NeighborsStatus() (*api.NeighborsStatusResponse, error) {

View File

@ -25,6 +25,7 @@ type Source interface {
ExpireCaches() int
Status() (*api.StatusResponse, error)
Neighbors() (*api.NeighborsResponse, error)
NeighborsSummary() (*api.NeighborsResponse, error)
NeighborsStatus() (*api.NeighborsStatusResponse, error)
Routes(neighborID string) (*api.RoutesResponse, error)
RoutesReceived(neighborID string) (*api.RoutesResponse, error)

View File

@ -2,6 +2,7 @@ package memory
import (
"context"
"errors"
"sync"
"github.com/alice-lg/alice-lg/pkg/api"
@ -97,6 +98,9 @@ func (b *NeighborsBackend) CountNeighborsAt(
) (int, error) {
neighbors, err := b.GetNeighborsAt(ctx, sourceID)
if err != nil {
if errors.Is(err, sources.ErrSourceNotFound) {
return 0, nil
}
return 0, err
}
return len(neighbors), nil

View File

@ -141,14 +141,23 @@ func (s *SourcesStore) ShouldRefresh(
log.Println("get status error:", err)
return false
}
nextRefresh := status.LastRefresh.Add(s.refreshInterval)
if status.State == StateBusy {
return false // Source is busy
}
nextRefresh := status.LastRefresh.Add(
s.refreshInterval)
if status.State == StateError {
// The refresh interval in the config is ok if the
// success case. When an error occures it is desireable
// to retry sooner, without spamming the server.
nextRefresh = status.LastRefresh.Add(10 * time.Second)
}
if time.Now().UTC().Before(nextRefresh) {
return false // Too soon
}
return true // Go for it
}