diff --git a/cmd/server/store.go b/cmd/server/store.go index 1261ea24..d3a30cee 100644 --- a/cmd/server/store.go +++ b/cmd/server/store.go @@ -1344,6 +1344,20 @@ func (s *PacketStore) MaxTransmissionID() int { return maxID } +// MaxObservationID returns the highest observation ID in the store. +func (s *PacketStore) MaxObservationID() int { + s.mu.RLock() + defer s.mu.RUnlock() + + maxID := 0 + for id := range s.byObsID { + if id > maxID { + maxID = id + } + } + return maxID +} + // --- Internal filter/query helpers --- // filterPackets applies PacketQuery filters to the in-memory packet list. diff --git a/cmd/server/websocket.go b/cmd/server/websocket.go index e4696bc4..96f544e1 100644 --- a/cmd/server/websocket.go +++ b/cmd/server/websocket.go @@ -166,6 +166,17 @@ func NewPoller(db *DB, hub *Hub, interval time.Duration) *Poller { func (p *Poller) Start() { lastID := p.db.GetMaxTransmissionID() lastObsID := p.db.GetMaxObservationID() + // If the store already loaded data, use its max IDs as a floor. + // This prevents replaying the entire DB when the DB query fails + // (e.g., corrupted DB returns 0 from COALESCE). + if p.store != nil { + if storeMax := p.store.MaxTransmissionID(); storeMax > lastID { + lastID = storeMax + } + if storeMaxObs := p.store.MaxObservationID(); storeMaxObs > lastObsID { + lastObsID = storeMaxObs + } + } log.Printf("[poller] starting from transmission ID %d, obs ID %d, interval %v", lastID, lastObsID, p.interval) ticker := time.NewTicker(p.interval)