Skip to content

Commit 41eb73a

Browse files
committed
Make the cache live longer for expensive queries
1 parent bfb5d4b commit 41eb73a

2 files changed

Lines changed: 27 additions & 6 deletions

File tree

backend/main.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,11 @@ async def startup():
195195
except Exception:
196196
logger.warning("iterations schema probe failed", exc_info=True)
197197

198+
# Pre-warm the Cost-tab overview cache in the background since it
199+
# takes a long time ro run.
200+
from backend.watch.services.system_tables import warm_cost_overview_cache
201+
asyncio.create_task(asyncio.to_thread(warm_cost_overview_cache, 7))
202+
198203

199204
@app.on_event("shutdown")
200205
async def shutdown():

backend/watch/services/system_tables.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
# ─── In-process TTL cache ─────────────────────────────────────────────────
3434
_CACHE_TTL_SECONDS = 300
35+
_LONG_CACHE_TTL_SECONDS = 1800
3536
_CACHE_MAX = 256
3637
_CACHE_LOCK = threading.Lock()
3738
_CACHE: dict[str, tuple[float, list[dict[str, Any]]]] = {}
@@ -70,13 +71,13 @@ def _cache_key(sql: str, parameters: list[StatementParameterListItem]) -> str:
7071
return f"{hash(sql)}|{json.dumps(bag)}"
7172

7273

73-
def _cache_get(key: str) -> list[dict[str, Any]] | None:
74+
def _cache_get(key: str, ttl_seconds: int = _CACHE_TTL_SECONDS) -> list[dict[str, Any]] | None:
7475
with _CACHE_LOCK:
7576
entry = _CACHE.get(key)
7677
if not entry:
7778
return None
7879
ts, rows = entry
79-
if time.monotonic() - ts > _CACHE_TTL_SECONDS:
80+
if time.monotonic() - ts > ttl_seconds:
8081
_CACHE.pop(key, None)
8182
return None
8283
return rows
@@ -90,6 +91,20 @@ def _cache_put(key: str, rows: list[dict[str, Any]]) -> None:
9091
_CACHE[key] = (time.monotonic(), rows)
9192

9293

94+
def warm_cost_overview_cache(days: int = 7) -> None:
95+
"""Pre-run the Cost-tab overview queries so the cache is hot before users
96+
hit the page. Call from a background task."""
97+
for fn, kwargs in (
98+
(workspace_summary, {"days": days}),
99+
(daily_volume_all_spaces, {"days": days}),
100+
(top_spenders, {"days": days, "limit": 10}),
101+
):
102+
try:
103+
fn(**kwargs)
104+
except Exception:
105+
logger.warning("warmup of %s failed", fn.__name__, exc_info=True)
106+
107+
93108
def _warehouse_id() -> str:
94109
wh = os.environ.get("SQL_WAREHOUSE_ID", "").strip()
95110
if not wh:
@@ -107,10 +122,11 @@ def _run(
107122
poll_total_seconds: int = 180,
108123
poll_interval_seconds: float = 2.0,
109124
track_health: bool = True,
125+
ttl_seconds: int = _CACHE_TTL_SECONDS,
110126
) -> list[dict[str, Any]]:
111127
global _SYSTEM_TABLES_ACCESSIBLE
112128
key = _cache_key(sql, parameters)
113-
cached = _cache_get(key)
129+
cached = _cache_get(key, ttl_seconds=ttl_seconds)
114130
if cached is not None:
115131
return cached
116132

@@ -363,7 +379,7 @@ def _workspace_names(workspace_ids: set[str]) -> dict[str, str]:
363379
def top_spenders(days: int = 7, limit: int = 10) -> list[dict[str, Any]]:
364380
params = [_p("days", days, "INT"), _p("limit", limit, "INT")]
365381
sql = _TOP_SPENDERS_SQL.format(ws=_ws_clause(params))
366-
rows = _run(sql, params)
382+
rows = _run(sql, params, ttl_seconds=_LONG_CACHE_TTL_SECONDS)
367383
workspace_ids = {r.get("workspace_id") for r in rows if r.get("workspace_id")}
368384
names = _workspace_names(workspace_ids)
369385
for r in rows:
@@ -579,7 +595,7 @@ def workspace_summary(days: int = 7) -> dict[str, Any]:
579595
# only a single ws_id param is appended.
580596
params = [_p("days", days, "INT")]
581597
sql = _WORKSPACE_SUMMARY_SQL.format(ws=_ws_clause(params))
582-
rows = _run(sql, params)
598+
rows = _run(sql, params, ttl_seconds=_LONG_CACHE_TTL_SECONDS)
583599
return rows[0] if rows else {}
584600

585601

@@ -610,7 +626,7 @@ def workspace_summary(days: int = 7) -> dict[str, Any]:
610626
def daily_volume_all_spaces(days: int = 30) -> list[dict[str, Any]]:
611627
params = [_p("days", days, "INT")]
612628
sql = _DAILY_VOLUME_ALL_SQL.format(ws=_ws_clause(params))
613-
return _run(sql, params)
629+
return _run(sql, params, ttl_seconds=_LONG_CACHE_TTL_SECONDS)
614630

615631

616632
_TOP_QUERIES_SQL = """

0 commit comments

Comments
 (0)