Record origin changes and show them on the prefix page (closes #161)
check / check (push) Successful in 1m3s

The routing table records a row each time a prefix and origin AS gets its first peer (announced) or loses its last (withdrawn), with the time and how many peers carry the prefix just after. The prefix handler writes the rows to a new origin_changes table, apart from routes. Every hour the database maintainer keeps each prefix's rows from the last 72 hours or its newest 500, whichever keeps more. The prefix page lists them newest first, at most 5,000.

At most 100,000 rows wait in memory to be written; past that the oldest are dropped and logged. A stop loses the rows past its last write.

Model: deepseek-v4-pro (implementation); opus-5-5 (commit message)
This commit was merged in pull request #231.
This commit is contained in:
clawbot committed 2026-11-05 06:57:41 +01:00
1 parent 15bf20a894
commit e58c6635e7
20 files changed
+1651 -88

No files matched your search

+27
View File
@@ -25,6 +25,10 @@ const (
// analyzeInterval is how often to run ANALYZE.
analyzeInterval = 1 * time.Hour
// originChangesPruneInterval is how often to prune the origin_changes
// table.
originChangesPruneInterval = 1 * time.Hour
// checkpointTimeout is the max time for WAL checkpoint.
checkpointTimeout = 10 * time.Second
@@ -33,6 +37,9 @@ const (
// analyzeTimeout is the max time for ANALYZE.
analyzeTimeout = 5 * time.Minute
// originChangesPruneTimeout is the max time for the origin changes prune.
originChangesPruneTimeout = 5 * time.Minute
)
// checkpointIncompleteMessage is logged, at debug level like a completed
@@ -80,6 +87,7 @@ func (m *DBMaintainer) Start(ctx context.Context) {
"checkpoint_interval", checkpointInterval,
"vacuum_interval", vacuumInterval,
"analyze_interval", analyzeInterval,
"origin_changes_prune_interval", originChangesPruneInterval,
)
}
@@ -111,6 +119,9 @@ func (m *DBMaintainer) run(ctx context.Context) {
analyzeTimer := time.NewTimer(analyzeInterval)
defer analyzeTimer.Stop()
pruneTimer := time.NewTimer(originChangesPruneInterval)
defer pruneTimer.Stop()
for {
select {
case <-m.stopCh:
@@ -127,6 +138,10 @@ func (m *DBMaintainer) run(ctx context.Context) {
case <-analyzeTimer.C:
m.runAnalyze(ctx)
analyzeTimer.Reset(analyzeInterval)
case <-pruneTimer.C:
m.runPruneOriginChanges(ctx)
pruneTimer.Reset(originChangesPruneInterval)
}
}
}
@@ -175,6 +190,18 @@ func (m *DBMaintainer) runAnalyze(ctx context.Context) {
}
}
// runPruneOriginChanges prunes the origin_changes table.
func (m *DBMaintainer) runPruneOriginChanges(ctx context.Context) {
duration, err := runWithTimeout(ctx, originChangesPruneTimeout,
m.db.PruneOriginChanges)
if err != nil {
m.logger.Error("Origin changes prune failed", "error", err,
"duration", duration)
} else {
m.logger.Debug("Origin changes pruned", "duration", duration)
}
}
// runWithTimeout runs operation with a context that ends after timeout and
// returns how long it took and its error.
func runWithTimeout(