Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions core/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1502,12 +1502,14 @@ func (s *APIServer) getRecentSeenMetrics(c *gin.Context) {
func (s *APIServer) getTotalSeenMetrics(c *gin.Context) {
stats := gin.H{}

totalFlights, err := s.cachedStats.GetCachedStat("total_flights")
var totalFlights int
err := s.pg.db.QueryRow(context.Background(),
"SELECT COUNT(*) FROM aircraft_data").Scan(&totalFlights)
if err == nil {
stats["total_flights"] = totalFlights
}

totalAircraft, err := s.cachedStats.GetCachedStat("total_aircraft")
totalAircraft, err := s.cachedStats.GetCachedTotalAircraft()
if err == nil {
stats["total_aircraft"] = totalAircraft
}
Expand Down
69 changes: 22 additions & 47 deletions core/cached-stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,7 @@ func NewCachedStatsService(pg *postgres) *CachedStatsService {
return &CachedStatsService{pg: pg}
}

func (s *CachedStatsService) GetCachedStat(key string) (int, error) {
var value int
err := s.pg.db.QueryRow(context.Background(),
"SELECT stat_value FROM cached_stats WHERE stat_key = $1", key).Scan(&value)
return value, err
}

func (s *CachedStatsService) UpdateCachedStat(key string, value int) error {
func (s *CachedStatsService) updateCachedStat(key string, value int) error {
_, err := s.pg.db.Exec(context.Background(),
`INSERT INTO cached_stats (stat_key, stat_value, last_updated)
VALUES ($1, $2, NOW())
Expand All @@ -32,59 +25,41 @@ func (s *CachedStatsService) UpdateCachedStat(key string, value int) error {
return err
}

func (s *CachedStatsService) RefreshAllTotals() error {
log.Info().Msg("Refreshing cached all-time statistics...")

// Update total flights
var totalFlights int
func (s *CachedStatsService) GetCachedTotalAircraft() (int, error) {
var value int
var lastUpdated time.Time
err := s.pg.db.QueryRow(context.Background(),
"SELECT COUNT(*) FROM aircraft_data").Scan(&totalFlights)
if err != nil {
log.Error().Err(err).Msg("Failed to count total flights")
return err
}
err = s.UpdateCachedStat("total_flights", totalFlights)
if err != nil {
log.Error().Err(err).Msg("Failed to update cached total_flights")
return err
`SELECT stat_value, last_updated FROM cached_stats
WHERE stat_key = 'total_aircraft'`).Scan(&value, &lastUpdated)

if err == nil && time.Since(lastUpdated) < 24*time.Hour {
log.Debug().
Int("total_aircraft", value).
Dur("cache_age", time.Since(lastUpdated)).
Msg("Returning cached total_aircraft")
return value, nil
}

// Update total aircraft
log.Info().Msg("Cache stale or missing, recalculating total_aircraft...")

var totalAircraft int
err = s.pg.db.QueryRow(context.Background(),
"SELECT COUNT(DISTINCT hex) FROM aircraft_data").Scan(&totalAircraft)
if err != nil {
log.Error().Err(err).Msg("Failed to count total aircraft")
return err
return 0, err
}
err = s.UpdateCachedStat("total_aircraft", totalAircraft)

// Update cache
err = s.updateCachedStat("total_aircraft", totalAircraft)
if err != nil {
log.Error().Err(err).Msg("Failed to update cached total_aircraft")
return err
return totalAircraft, nil
}

log.Info().
Int("total_flights", totalFlights).
Int("total_aircraft", totalAircraft).
Msg("Successfully refreshed cached statistics")

return nil
}

func (s *CachedStatsService) StartPeriodicRefresh(interval time.Duration) {
go func() {
// Initial refresh on startup
if err := s.RefreshAllTotals(); err != nil {
log.Error().Err(err).Msg("Initial cache refresh failed")
}

ticker := time.NewTicker(interval)
defer ticker.Stop()
Msg("Successfully recalculated and cached total_aircraft")

for range ticker.C {
if err := s.RefreshAllTotals(); err != nil {
log.Error().Err(err).Msg("Periodic cache refresh failed")
}
}
}()
return totalAircraft, nil
}
7 changes: 2 additions & 5 deletions core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,10 @@ func main() {
os.Exit(1)
}

apiServer := NewAPIServer(pg)

log.Info().Msg("Starting cached statistics refresh service")
apiServer.cachedStats.StartPeriodicRefresh(24 * time.Hour)

// Start API server in a separate goroutine
log.Info().Msg("Starting API server")
apiServer := NewAPIServer(pg)

go func() {
apiServer.Start()
}()
Expand Down
1 change: 0 additions & 1 deletion migrations/000008_add_cached_stats.up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,4 @@ CREATE TABLE cached_stats (
-- Insert initial values for all-time totals
INSERT INTO cached_stats (stat_key, stat_value, last_updated)
VALUES
('total_flights', (SELECT COUNT(*) FROM aircraft_data), NOW()),
('total_aircraft', (SELECT COUNT(DISTINCT hex) FROM aircraft_data), NOW());
5 changes: 5 additions & 0 deletions web/src/components/MetricAircraftSeen.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
let loading = true;
let error = null;
let recentInterval = null;
let totalInterval = null;

async function fetchRecentData() {
try {
Expand Down Expand Up @@ -45,12 +46,16 @@
fetchRecentData();
fetchTotalData();
recentInterval = setInterval(fetchRecentData, 2000);
totalInterval = setInterval(fetchTotalData, 60000);
})

onDestroy(() => {
if (recentInterval) {
clearInterval(recentInterval);
}
if (totalInterval) {
clearInterval(totalInterval);
}
});
</script>
{#if loading}
Expand Down
5 changes: 5 additions & 0 deletions web/src/components/MetricFlightsSeen.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
let loading = true;
let error = null;
let recentInterval = null;
let totalInterval = null;

async function fetchRecentData() {
try {
Expand Down Expand Up @@ -45,12 +46,16 @@
fetchRecentData();
fetchTotalData();
recentInterval = setInterval(fetchRecentData, 2000);
totalInterval = setInterval(fetchTotalData, 60000);
})

onDestroy(() => {
if (recentInterval) {
clearInterval(recentInterval);
}
if (totalInterval) {
clearInterval(totalInterval);
}
});
</script>
{#if loading}
Expand Down
Loading