Write the routing table every second and read it at startup (part of #122)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
The prefix handler applies each update to the routing table in memory as it handles it, sorted into announcements and withdrawals as for the live route tables. Each flush first writes at most 20,000 of the table's changed pairs, with every new peer; the rest follow in the next flushes. A failed write puts its changes and new peers back in the table. Stop makes one last flush, and logs the changed pairs left. Startup reads the table before the streamer connects, and logs its pairs, peers and time. The per-peer counts are a slice, so the read takes about a third less time. The health check's start period is 60 seconds, so a full table's read fits before upaas looks. Model: opus-5-5
This commit was merged in pull request #187.
This commit is contained in:
1 parent
eda37da369
commit
bac531c76d
16 files changed
+1023
-114
No files matched your search
@@ -13,6 +13,7 @@ import (
|
||||
"git.eeqj.de/sneak/routewatch/internal/database"
|
||||
"git.eeqj.de/sneak/routewatch/internal/logger"
|
||||
"git.eeqj.de/sneak/routewatch/internal/ristypes"
|
||||
"git.eeqj.de/sneak/routewatch/internal/routetable"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -119,10 +120,11 @@ func (s *blockingStore) releaseWrites() {
|
||||
//
|
||||
//nolint:tagliatelle // field names are the keys the handlers log
|
||||
type stopLog struct {
|
||||
Msg string `json:"msg"`
|
||||
Updates int `json:"updates"`
|
||||
PeersDown int `json:"peers_down"`
|
||||
Paths int `json:"paths"`
|
||||
Msg string `json:"msg"`
|
||||
Updates int `json:"updates"`
|
||||
PeersDown int `json:"peers_down"`
|
||||
TableChanges int `json:"table_changes"`
|
||||
Paths int `json:"paths"`
|
||||
}
|
||||
|
||||
// newErrorLogger returns a logger that writes only errors, as JSON lines, to
|
||||
@@ -313,6 +315,9 @@ func stopWhileAWriteIsBlocked(
|
||||
}
|
||||
}
|
||||
|
||||
// TestPrefixHandlerSkipsItsLastWriteAfterTheStopDeadline also checks the
|
||||
// routing table's changes: the flush whose delete is held wrote those of the
|
||||
// fill, so Stop must log the one of the extra prefix as not written.
|
||||
func TestPrefixHandlerSkipsItsLastWriteAfterTheStopDeadline(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -321,7 +326,8 @@ func TestPrefixHandlerSkipsItsLastWriteAfterTheStopDeadline(t *testing.T) {
|
||||
var logs bytes.Buffer
|
||||
|
||||
store := newBlockingStore()
|
||||
h := NewPrefixHandler(store, newErrorLogger(&logs))
|
||||
h := NewPrefixHandler(store, newErrorLogger(&logs),
|
||||
routetable.New(routetable.Params{}))
|
||||
fill := slices.Repeat([]string{testV4Prefix}, prefixBatchSize)
|
||||
// The peer's session goes down first, so the write held is the delete of
|
||||
// its routes, and Stop must log that peer down too.
|
||||
@@ -337,9 +343,51 @@ func TestPrefixHandlerSkipsItsLastWriteAfterTheStopDeadline(t *testing.T) {
|
||||
}
|
||||
|
||||
logged := lastLogged(t, &logs)
|
||||
if logged.Updates != prefixBatchSize+1 || logged.PeersDown != 1 {
|
||||
t.Errorf("Stop logged %d updates and %d peer downs not written, "+
|
||||
"want %d and 1", logged.Updates, logged.PeersDown, prefixBatchSize+1)
|
||||
if logged.Updates != prefixBatchSize+1 || logged.PeersDown != 1 ||
|
||||
logged.TableChanges != 1 {
|
||||
t.Errorf("Stop logged %d updates, %d peer downs and %d table changes "+
|
||||
"not written, want %d, 1 and 1", logged.Updates, logged.PeersDown,
|
||||
logged.TableChanges, prefixBatchSize+1)
|
||||
}
|
||||
}
|
||||
|
||||
// tableWriteBlockingStore is a blockingStore whose writes of the routing
|
||||
// table's changes also wait until the test calls releaseWrites.
|
||||
type tableWriteBlockingStore struct {
|
||||
*blockingStore
|
||||
}
|
||||
|
||||
func (s tableWriteBlockingStore) WriteRouteChanges(
|
||||
changes []routetable.Change, newPeers map[string]uint,
|
||||
) error {
|
||||
s.waitForRelease()
|
||||
|
||||
return s.mockStore.WriteRouteChanges(changes, newPeers)
|
||||
}
|
||||
|
||||
// TestPrefixHandlerStopCountsTheTableChangesOfAWriteInProgress holds the write
|
||||
// of the routing table's changes that a full batch starts, then hands the
|
||||
// prefix handler one more prefix and stops it with a deadline that passes
|
||||
// meanwhile. Stop must log as not written both the change the held write took
|
||||
// and the one since.
|
||||
func TestPrefixHandlerStopCountsTheTableChangesOfAWriteInProgress(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const extraPrefix = "198.51.100.0/24"
|
||||
|
||||
var logs bytes.Buffer
|
||||
|
||||
blocking := newBlockingStore()
|
||||
h := NewPrefixHandler(tableWriteBlockingStore{blockingStore: blocking},
|
||||
newErrorLogger(&logs), routetable.New(routetable.Params{}))
|
||||
fill := slices.Repeat([]string{testV4Prefix}, prefixBatchSize)
|
||||
stopWhileAWriteIsBlocked(t, blocking, h, h.done,
|
||||
[]*ristypes.RISMessage{prefixAnnouncement(fill)},
|
||||
prefixAnnouncement([]string{extraPrefix}))
|
||||
|
||||
if logged := lastLogged(t, &logs); logged.TableChanges != 2 {
|
||||
t.Errorf("Stop logged %d table changes not written, want 2",
|
||||
logged.TableChanges)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -455,7 +503,7 @@ func TestPrefixHandlerHandlesMessagesWhileAWriteIsBlocked(t *testing.T) {
|
||||
const extraPrefix = "198.51.100.0/24"
|
||||
|
||||
store := newBlockingStore()
|
||||
h := NewPrefixHandler(store, logger.New())
|
||||
h := NewPrefixHandler(store, logger.New(), routetable.New(routetable.Params{}))
|
||||
fill := slices.Repeat([]string{testV4Prefix}, prefixBatchSize)
|
||||
handleWhileAWriteIsBlocked(t, store, h,
|
||||
[]*ristypes.RISMessage{prefixAnnouncement(fill)},
|
||||
@@ -515,7 +563,7 @@ func TestPrefixHandlerWaitsAtTheMaximumBatchSize(t *testing.T) {
|
||||
const extraPrefix = "198.51.100.0/24"
|
||||
|
||||
store := newBlockingStore()
|
||||
h := NewPrefixHandler(store, logger.New())
|
||||
h := NewPrefixHandler(store, logger.New(), routetable.New(routetable.Params{}))
|
||||
fill := slices.Repeat([]string{testV4Prefix}, prefixBatchSize)
|
||||
toMax := slices.Repeat([]string{testV4Prefix}, prefixBatchMaxSize)
|
||||
handleAtMaxBatchSize(t, store, h,
|
||||
@@ -592,7 +640,10 @@ func TestHandlersWakeTheFlushGoroutineWhenTheBatchIsFull(t *testing.T) {
|
||||
name: "prefix",
|
||||
batchSize: prefixBatchSize,
|
||||
handle: func(n int) chan struct{} {
|
||||
h := &PrefixHandler{batchFullCh: make(chan struct{}, 1)}
|
||||
h := &PrefixHandler{
|
||||
batchFullCh: make(chan struct{}, 1),
|
||||
table: routetable.New(routetable.Params{}),
|
||||
}
|
||||
h.HandleMessage(prefixAnnouncement(
|
||||
slices.Repeat([]string{testV4Prefix}, n)))
|
||||
|
||||
|
||||
Reference in new issue
Block a user