diff --git a/backend/app/api/forgejo_metrics.py b/backend/app/api/forgejo_metrics.py index 7669720..a8b3f84 100644 --- a/backend/app/api/forgejo_metrics.py +++ b/backend/app/api/forgejo_metrics.py @@ -383,6 +383,10 @@ async def get_forgejo_heatmap( raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) since = utcnow() - timedelta(days=365) + # Line-stats fetch uses a shorter window — querying a full year of commits + # with stat=true paginates hundreds of pages and takes 30+ seconds on active + # repos. 90 days is enough context for the dashboard summary numbers. + line_stats_since = utcnow() - timedelta(days=90) # Fetch repos with their connections in one query repos_with_conns = ( @@ -441,7 +445,7 @@ async def get_forgejo_heatmap( # Line stats — served from cache; background task refreshes when stale. # Extract plain values NOW while the DB session is still open. - since_iso = since.strftime("%Y-%m-%dT%H:%M:%SZ") + since_iso = line_stats_since.strftime("%Y-%m-%dT%H:%M:%SZ") cache_key = str(ctx.organization.id) cached = _line_stats_cache.get(cache_key) now = _time.monotonic() diff --git a/backend/app/services/forgejo_client.py b/backend/app/services/forgejo_client.py index 5038f04..1265137 100644 --- a/backend/app/services/forgejo_client.py +++ b/backend/app/services/forgejo_client.py @@ -380,7 +380,10 @@ class ForgejoAPIClient: client = await self._get_client() total_adds = total_dels = 0 page = 1 - while True: + # Cap at 5 pages (250 commits) — prevents unbounded pagination on + # very active repos where each page requires Forgejo to compute + # per-commit diffs (?stat=true), which is the primary latency driver. + while page <= 5: response = await client.get( f"/api/v1/repos/{owner}/{repo}/commits", params={"since": since_iso, "limit": 50, "page": page, "stat": "true"},