From addd616d59337e676087b4b01cd856071a864794 Mon Sep 17 00:00:00 2001 From: Mazyar Date: Tue, 30 Jun 2026 22:24:04 +0330 Subject: [PATCH] Enhance dashboard statistics loading with cold-cache handling - Introduced a new constant, statisticsColdLoadWait, to define the wait time for cold-cache requests before serving a placeholder response. - Updated the Statistics method to initiate a background load for statistics while allowing for a brief wait for real data, improving responsiveness during cache warming. - Implemented a channel to handle the result of the background loading process, ensuring that users receive timely feedback while the cache is being populated. This update optimizes the dashboard's performance by ensuring that users are served quickly, even when the statistics are being freshly loaded from the backend. --- internal/dashboard/service.go | 39 ++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/internal/dashboard/service.go b/internal/dashboard/service.go index 2bdef2a..6487cb2 100644 --- a/internal/dashboard/service.go +++ b/internal/dashboard/service.go @@ -25,6 +25,11 @@ const ( summaryCacheTTL = 60 * time.Second statisticsCacheTTL = 5 * time.Minute statisticsStaleGraceTTL = 15 * time.Minute + // statisticsColdLoadWait bounds how long a cold-cache request waits for a fresh + // load before falling back to a placeholder. Mongo-only loads (MinIO scope + // already cached) comfortably finish within this window; a first-ever MinIO + // bucket scan does not, so we don't block the request for that. + statisticsColdLoadWait = 4 * time.Second ) // Service defines dashboard business operations. @@ -224,13 +229,33 @@ func (s *service) Statistics(ctx context.Context, query StatisticsQuery) (*Stati } // Cold cache: the underlying load performs a full MinIO bucket scan plus Mongo - // aggregations that can take minutes. Never block the request on it. Kick off a - // background refresh (deduped via singleflight) and serve an empty report so the - // endpoint stays fast; subsequent requests get full data once the cache warms. - go s.refreshStatistics(days) - s.logger.Info("Serving placeholder dashboard statistics while cache warms", map[string]interface{}{ - "days": days, - }) + // aggregations that can take minutes on a first run. Start the load in the + // background (deduped via singleflight, and detached from this request's + // context so it keeps running even if we give up waiting) and wait briefly for + // it. If it finishes in time we return real data; otherwise we serve a + // placeholder so the endpoint stays fast, and the load keeps warming the cache + // for the next request. + resultCh := make(chan *StatisticsReportResponse, 1) + go func() { + result, err := s.loadStatistics(context.Background(), days) + if err != nil { + resultCh <- nil + return + } + resultCh <- result + }() + + select { + case result := <-resultCh: + if result != nil { + return result, nil + } + case <-time.After(statisticsColdLoadWait): + s.logger.Info("Serving placeholder dashboard statistics while cache warms", map[string]interface{}{ + "days": days, + }) + } + return emptyStatisticsReport(days), nil }