Skip to content

Commit e026a5b

Browse files
authored
fix: keep metrics page consistent with the weekly email (#1962)
* fix(metrics): correct dashboard week boundary to Saturday The dashboard window ended on Sunday instead of Saturday, so mid-week it requested a different window than the email and missed the warmed cache key. Align the exclusive end to the most recent Saturday so the page matches the email window and hits the warm cache. Part of #1961 Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi> * fix(metrics): warm the metrics cache on server startup Warm the cache on web-server startup (gunicorn/runserver only, single-flight via a Redis lock) so a mid-week deploy doesn't leave the page on the short-lived cache until the next Saturday cron. Align the warm window to the latest Saturday so it targets the same Sat-Fri window the dashboard requests instead of a rolling 7-day window. Part of #1961 Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi> * fix(metrics): drop HTTP cache on metrics endpoint Query-level cache in get_metrics_data is enough; view_cache added a second layer that could serve stale responses after the warmed query cache updates. Closes #1961 Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi> --------- Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
1 parent 5ef7b2b commit e026a5b

4 files changed

Lines changed: 37 additions & 18 deletions

File tree

backend/kernelCI_app/apps.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import os
2+
import sys
3+
import threading
4+
15
from django.apps import AppConfig
26

37

@@ -6,4 +10,20 @@ class KernelciAppConfig(AppConfig):
610
name = "kernelCI_app"
711

812
def ready(self) -> None:
9-
return super().ready()
13+
super().ready()
14+
is_server = (
15+
os.path.basename(sys.argv[0]) == "gunicorn" or "runserver" in sys.argv
16+
)
17+
if not is_server:
18+
return
19+
if "runserver" in sys.argv and os.environ.get("RUN_MAIN") != "true":
20+
return
21+
22+
from django.core.cache import cache
23+
24+
from kernelCI_app.queries.notifications import warm_metrics_cache
25+
26+
# Single-flight via atomic Redis add: only one worker warms per deploy.
27+
if not cache.add("metrics_startup_warm_lock", 1, timeout=600):
28+
return
29+
threading.Thread(target=warm_metrics_cache, daemon=True).start()

backend/kernelCI_app/queries/notifications.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import sys
22
from concurrent.futures import ThreadPoolExecutor
3-
from datetime import datetime, time, timedelta, timezone
3+
from datetime import date, datetime, time, timedelta, timezone
44
from typing import Any
55

66
from django.db import connection, connections
@@ -749,6 +749,12 @@ def query_fetchall_work(
749749
return rows
750750

751751

752+
def last_saturday(today: date | None = None) -> date:
753+
"""Most recent Saturday UTC (today when today is Saturday)."""
754+
day = today or datetime.now(timezone.utc).date()
755+
return day - timedelta(days=(day.weekday() + 2) % 7)
756+
757+
752758
def interval_params(start_days_ago: int, end_days_ago: int) -> dict[str, str]:
753759
"""Build [start_date, end_date) bounds from UTC day offsets."""
754760
today = datetime.now(timezone.utc).date()
@@ -1068,16 +1074,17 @@ def get_metrics_data(
10681074

10691075

10701076
def warm_metrics_cache() -> None:
1077+
today = datetime.now(timezone.utc).date()
1078+
end_days_ago = (today - last_saturday(today)).days
10711079
for period_days in METRICS_CACHE_WARM_PERIODS:
10721080
out(
1073-
"Warming metrics cache for "
1074-
f"{period_days}-day period "
1075-
f"(start_days_ago={period_days}, end_days_ago=0)"
1081+
f"Warming metrics cache for {period_days}-day Sat-Fri period "
1082+
f"(end_days_ago={end_days_ago})"
10761083
)
10771084
try:
10781085
get_metrics_data(
1079-
start_days_ago=period_days,
1080-
end_days_ago=0,
1086+
start_days_ago=end_days_ago + period_days,
1087+
end_days_ago=end_days_ago,
10811088
use_cache=False,
10821089
cache_timeout=METRICS_CACHE_WARM_TIMEOUT,
10831090
)

backend/kernelCI_app/urls.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
)
99

1010
from kernelCI_app import views
11-
from kernelCI_app.queries.notifications import METRICS_CACHE_TIMEOUT
1211

1312

1413
def view_cache(view, timeout: int = settings.CACHE_TIMEOUT):
@@ -180,9 +179,5 @@ def view_cache(view, timeout: int = settings.CACHE_TIMEOUT):
180179
path("proxy/", views.ProxyView.as_view(), name="proxyView"),
181180
path("origins/", views.OriginsView.as_view(), name="originsView"),
182181
path("tree-report/", views.TreeReport.as_view(), name="treeReportView"),
183-
path(
184-
"metrics/",
185-
view_cache(views.MetricsView, timeout=METRICS_CACHE_TIMEOUT),
186-
name="metricsView",
187-
),
182+
path("metrics/", views.MetricsView.as_view(), name="metricsView"),
188183
]

dashboard/src/pages/Metrics/MetricsPage.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -551,11 +551,8 @@ function LabsSection({ labs }: { labs: LabData[] }): JSX.Element {
551551
const getMetricsQueryParams = (
552552
activeDays: number,
553553
): { startDaysAgo: number; endDaysAgo: number } => {
554-
const today = new Date();
555-
const weekEndDaysAgo = (today.getUTCDay() + 1) % DAYS_IN_WEEK;
556-
const endDaysAgo = Math.max(weekEndDaysAgo - 1, 0);
557-
const startDaysAgo = endDaysAgo + activeDays;
558-
return { startDaysAgo, endDaysAgo };
554+
const endDaysAgo = (new Date().getUTCDay() + 1) % DAYS_IN_WEEK;
555+
return { startDaysAgo: endDaysAgo + activeDays, endDaysAgo };
559556
};
560557

561558
export const MetricsPage = (): JSX.Element => {

0 commit comments

Comments
 (0)