Fix shutdown sequence to ensure final snapshot is taken

- Add Shutdown() method to RouteWatch with mutex-protected shutdown flag
- Move all cleanup logic from Run() to Shutdown()
- Call Shutdown() from fx OnStop hook
- This ensures snapshotter gets called during graceful shutdown
This commit is contained in:
Jeffrey Paul 2025-07-28 00:11:54 +02:00
parent 52cdcd5785
commit fa9b086629
2 changed files with 21 additions and 3 deletions

View File

@ -8,6 +8,7 @@ import (
"log/slog" "log/slog"
"os" "os"
"strings" "strings"
"sync"
"time" "time"
"git.eeqj.de/sneak/routewatch/internal/database" "git.eeqj.de/sneak/routewatch/internal/database"
@ -58,6 +59,8 @@ type RouteWatch struct {
snapshotter *snapshotter.Snapshotter snapshotter *snapshotter.Snapshotter
logger *slog.Logger logger *slog.Logger
maxRuntime time.Duration maxRuntime time.Duration
shutdown bool
mu sync.Mutex
} }
// New creates a new RouteWatch instance // New creates a new RouteWatch instance
@ -130,6 +133,20 @@ func (rw *RouteWatch) Run(ctx context.Context) error {
// Wait for context cancellation // Wait for context cancellation
<-ctx.Done() <-ctx.Done()
return nil
}
// Shutdown performs graceful shutdown of all services
func (rw *RouteWatch) Shutdown() {
rw.mu.Lock()
if rw.shutdown {
rw.mu.Unlock()
return
}
rw.shutdown = true
rw.mu.Unlock()
// Stop services // Stop services
rw.streamer.Stop() rw.streamer.Stop()
@ -153,15 +170,15 @@ func (rw *RouteWatch) Run(ctx context.Context) error {
// Take final snapshot before shutdown if snapshotter is available // Take final snapshot before shutdown if snapshotter is available
if rw.snapshotter != nil { if rw.snapshotter != nil {
rw.logger.Info("Shutting down snapshotter") rw.logger.Info("Taking final snapshot before shutdown")
if err := rw.snapshotter.Shutdown(); err != nil { if err := rw.snapshotter.Shutdown(); err != nil {
rw.logger.Error("Failed to shutdown snapshotter", "error", err) rw.logger.Error("Failed to shutdown snapshotter", "error", err)
} else {
rw.logger.Info("Final snapshot completed")
} }
} else { } else {
rw.logger.Info("No snapshotter available") rw.logger.Info("No snapshotter available")
} }
return nil
} }
// logRoutingTableStats periodically logs routing table statistics // logRoutingTableStats periodically logs routing table statistics

View File

@ -40,6 +40,7 @@ func CLIEntry() {
}, },
OnStop: func(_ context.Context) error { OnStop: func(_ context.Context) error {
logger.Info("Shutting down RouteWatch") logger.Info("Shutting down RouteWatch")
rw.Shutdown()
return nil return nil
}, },