Skip to content

Commit 8aeb0d5

Browse files
committed
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>
1 parent 78b86f2 commit 8aeb0d5

2 files changed

Lines changed: 34 additions & 7 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
)

0 commit comments

Comments
 (0)