Implement batched database operations for improved performance

- Add BatchedDatabaseHandler that batches prefix, ASN, and peering operations
- Add BatchedPeerHandler that batches peer update operations
- Batch operations are deduped and flushed every 100-200ms or when batch size is reached
- Add EnableBatchedDatabaseWrites config option (enabled by default)
- Properly flush remaining batches on shutdown
- This significantly reduces database write pressure and improves throughput
This commit is contained in:
2025-07-28 01:01:27 +02:00
parent d15a5e91b9
commit 155c08d735
4 changed files with 485 additions and 17 deletions

View File

@@ -41,15 +41,18 @@ type Dependencies struct {
// RouteWatch represents the main application instance
type RouteWatch struct {
db database.Store
routingTable *routingtable.RoutingTable
streamer *streamer.Streamer
server *server.Server
snapshotter *snapshotter.Snapshotter
logger *slog.Logger
maxRuntime time.Duration
shutdown bool
mu sync.Mutex
db database.Store
routingTable *routingtable.RoutingTable
streamer *streamer.Streamer
server *server.Server
snapshotter *snapshotter.Snapshotter
logger *slog.Logger
maxRuntime time.Duration
shutdown bool
mu sync.Mutex
config *config.Config
batchedDBHandler *BatchedDatabaseHandler
batchedPeerHandler *BatchedPeerHandler
}
// isTruthy returns true if the value is considered truthy
@@ -72,6 +75,7 @@ func New(deps Dependencies) *RouteWatch {
server: deps.Server,
logger: deps.Logger,
maxRuntime: deps.Config.MaxRuntime,
config: deps.Config,
}
// Create snapshotter if enabled
@@ -101,17 +105,25 @@ func (rw *RouteWatch) Run(ctx context.Context) error {
}
// Register database handler to process BGP UPDATE messages
dbHandler := NewDatabaseHandler(rw.db, rw.logger)
rw.streamer.RegisterHandler(dbHandler)
if rw.config.EnableBatchedDatabaseWrites {
rw.logger.Info("Using batched database handlers for improved performance")
rw.batchedDBHandler = NewBatchedDatabaseHandler(rw.db, rw.logger)
rw.streamer.RegisterHandler(rw.batchedDBHandler)
rw.batchedPeerHandler = NewBatchedPeerHandler(rw.db, rw.logger)
rw.streamer.RegisterHandler(rw.batchedPeerHandler)
} else {
dbHandler := NewDatabaseHandler(rw.db, rw.logger)
rw.streamer.RegisterHandler(dbHandler)
peerHandler := NewPeerHandler(rw.db, rw.logger)
rw.streamer.RegisterHandler(peerHandler)
}
// Register routing table handler to maintain in-memory routing table
rtHandler := NewRoutingTableHandler(rw.routingTable, rw.logger)
rw.streamer.RegisterHandler(rtHandler)
// Register peer tracking handler to track all peers
peerHandler := NewPeerHandler(rw.db, rw.logger)
rw.streamer.RegisterHandler(peerHandler)
// Start periodic routing table stats logging
go rw.logRoutingTableStats(ctx)
@@ -147,6 +159,16 @@ func (rw *RouteWatch) Shutdown() {
rw.shutdown = true
rw.mu.Unlock()
// Stop batched handlers first to flush remaining batches
if rw.batchedDBHandler != nil {
rw.logger.Info("Flushing batched database handler")
rw.batchedDBHandler.Stop()
}
if rw.batchedPeerHandler != nil {
rw.logger.Info("Flushing batched peer handler")
rw.batchedPeerHandler.Stop()
}
// Stop services
rw.streamer.Stop()