Re-vendor the shared files from sneak/prompts at 0653579 (closes #52)
check / check (push) Canceled after 0s

Takes .golangci.yml, .dockerignore, .gitignore, .editorconfig, the
workflow and REPO_POLICIES.md from sneak/prompts 0653579, with this
repository's own entries after the shared content. Lint (golangci-lint
v2.14.0) and test are Dockerfile phases that script/lint and script/test
build alone with a new CACHEBUST value, keeping Go's and golangci-lint's
caches; the image build depends on both. make fmt and make fmt-check also
format the markdown with prettier, pinned in yarn.lock. The agent
guidance moves to AGENTS.md. The Go code is fixed for what the new
configuration finds, and the AS and prefix detail reads now return the
rows' error after their loops.

Model: opus-5-5
This commit was merged in pull request #85.
This commit is contained in:
2026-10-19 12:10:11 +02:00
parent 4f515d718b
commit b89987e356
65 changed files with 5624 additions and 3509 deletions
+34 -16
View File
@@ -1,10 +1,13 @@
// Package routewatch contains the primary RouteWatch type that represents a running instance
// of the application and contains pointers to its core dependencies, and is responsible for initialization.
// Package routewatch contains the primary RouteWatch type that represents a
// running instance of the application and contains pointers to its core
// dependencies, and is responsible for initialization. It also contains the
// ASN WHOIS fetcher for background updates and the database maintainer for
// background maintenance tasks.
package routewatch
import (
"context"
"fmt"
"errors"
"sync"
"time"
@@ -18,6 +21,10 @@ import (
"go.uber.org/fx"
)
// errNonBatchedNotImplemented is returned by Run when batched database writes
// are disabled, since only the batched handlers exist.
var errNonBatchedNotImplemented = errors.New("non-batched handlers not implemented")
// Dependencies contains all dependencies for RouteWatch
type Dependencies struct {
fx.In
@@ -68,8 +75,10 @@ func (rw *RouteWatch) Run(ctx context.Context) error {
// Apply runtime limit if specified
if rw.maxRuntime > 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, rw.maxRuntime)
defer cancel()
rw.logger.Info("Running with time limit", "max_runtime", rw.maxRuntime)
}
@@ -96,29 +105,31 @@ func (rw *RouteWatch) Run(ctx context.Context) error {
// Non-batched handlers not implemented yet
rw.logger.Error("Non-batched handlers not implemented")
return fmt.Errorf("non-batched handlers not implemented")
return errNonBatchedNotImplemented
}
// No longer need routing table handler - PrefixHandler maintains live_routes table
// Start streaming
if err := rw.streamer.Start(); err != nil {
err := rw.streamer.Start(ctx)
if err != nil {
return err
}
// Start HTTP server
if err := rw.server.Start(); err != nil {
err = rw.server.Start(ctx)
if err != nil {
return err
}
// Start ASN WHOIS fetcher for background updates
rw.asnFetcher = NewASNFetcher(rw.db, rw.logger.Logger)
rw.asnFetcher.Start()
rw.asnFetcher.Start(ctx)
rw.server.SetASNFetcher(rw.asnFetcher)
// Start database maintenance goroutine
rw.dbMaintainer = NewDBMaintainer(rw.db, rw.logger.Logger)
rw.dbMaintainer.Start()
rw.dbMaintainer.Start(ctx)
// Wait for context cancellation
<-ctx.Done()
@@ -127,13 +138,14 @@ func (rw *RouteWatch) Run(ctx context.Context) error {
}
// Shutdown performs graceful shutdown of all services
func (rw *RouteWatch) Shutdown() {
func (rw *RouteWatch) Shutdown(ctx context.Context) {
rw.mu.Lock()
if rw.shutdown {
rw.mu.Unlock()
return
}
rw.shutdown = true
rw.mu.Unlock()
@@ -142,14 +154,17 @@ func (rw *RouteWatch) Shutdown() {
rw.logger.Info("Flushing database handler")
rw.dbHandler.Stop()
}
if rw.peerHandler != nil {
rw.logger.Info("Flushing peer handler")
rw.peerHandler.Stop()
}
if rw.prefixHandler != nil {
rw.logger.Info("Flushing prefix handler")
rw.prefixHandler.Stop()
}
if rw.peeringHandler != nil {
rw.logger.Info("Flushing peering handler")
rw.peeringHandler.Stop()
@@ -168,11 +183,15 @@ func (rw *RouteWatch) Shutdown() {
// Stop services
rw.streamer.Stop()
// Stop HTTP server with a timeout
// Stop HTTP server with a timeout of its own, however much of ctx is left
const serverStopTimeout = 5 * time.Second
stopCtx, cancel := context.WithTimeout(context.Background(), serverStopTimeout)
stopCtx, cancel := context.WithTimeout(
context.WithoutCancel(ctx), serverStopTimeout)
defer cancel()
if err := rw.server.Stop(stopCtx); err != nil {
err := rw.server.Stop(stopCtx)
if err != nil {
rw.logger.Error("Failed to stop HTTP server gracefully", "error", err)
}
@@ -185,12 +204,11 @@ func (rw *RouteWatch) Shutdown() {
"bits_per_sec", metrics.BitsPerSec,
"duration", time.Since(metrics.ConnectedSince),
)
}
// getModule provides all fx dependencies
func getModule() fx.Option {
return fx.Options(
func getModule() []fx.Option {
return []fx.Option{
fx.Provide(
logger.New,
config.New,
@@ -203,5 +221,5 @@ func getModule() fx.Option {
server.New,
New,
),
)
}
}