From 74ad04abc29b266b929386c7e67ada679509d70b Mon Sep 17 00:00:00 2001 From: Mark Saroufim Date: Fri, 6 Feb 2026 21:07:25 -0800 Subject: [PATCH] Add in-memory caching to leaderboard endpoints Cache responses from /api/leaderboard-summaries and /api/leaderboard/ with a 60-second TTL to avoid running expensive SQL queries on every request. Follows the same pattern used in the events API. --- kernelboard/api/leaderboard.py | 13 +++++++++++++ kernelboard/api/leaderboard_summaries.py | 22 +++++++++++++++++++--- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/kernelboard/api/leaderboard.py b/kernelboard/api/leaderboard.py index 2cb6a20b..42b3988d 100644 --- a/kernelboard/api/leaderboard.py +++ b/kernelboard/api/leaderboard.py @@ -1,3 +1,4 @@ +import time from typing import Any from flask import Blueprint from kernelboard.lib.db import get_db_connection @@ -8,9 +9,18 @@ leaderboard_bp = Blueprint("leaderboard_bp", __name__, url_prefix="/leaderboard") +# Simple in-memory cache keyed by leaderboard_id +_cache: dict[int, dict] = {} +CACHE_TTL_SECONDS = 60 + @leaderboard_bp.route("/", methods=["GET"]) def leaderboard(leaderboard_id: int): + now = time.time() + cached = _cache.get(leaderboard_id) + if cached is not None and (now - cached["timestamp"]) < CACHE_TTL_SECONDS: + return http_success(cached["data"]) + conn = get_db_connection() query = _get_query() with conn.cursor() as cur: @@ -27,6 +37,9 @@ def leaderboard(leaderboard_id: int): data = result[0] res = to_api_leaderboard_item(data) + + _cache[leaderboard_id] = {"data": res, "timestamp": now} + return http_success(res) diff --git a/kernelboard/api/leaderboard_summaries.py b/kernelboard/api/leaderboard_summaries.py index 0144c39c..9417d21b 100644 --- a/kernelboard/api/leaderboard_summaries.py +++ b/kernelboard/api/leaderboard_summaries.py @@ -1,3 +1,5 @@ +import time + from flask import Blueprint from datetime import datetime, timezone from kernelboard.lib.db import get_db_connection @@ -8,6 +10,13 @@ "leaderboard_summaries_bp", __name__, url_prefix="/leaderboard-summaries" ) +# Simple in-memory cache +_cache = { + "data": None, + "timestamp": 0, +} +CACHE_TTL_SECONDS = 60 + @leaderboard_summaries_bp.route("", methods=["GET"]) def index(): @@ -28,6 +37,10 @@ def index(): # ], # } + now = time.time() + if _cache["data"] is not None and (now - _cache["timestamp"]) < CACHE_TTL_SECONDS: + return http_success(_cache["data"]) + conn = get_db_connection() query = _get_query() with conn.cursor() as cur: @@ -38,9 +51,12 @@ def index(): if lb["gpu_types"] is None: lb["gpu_types"] = [] - return http_success( - {"leaderboards": leaderboards, "now": datetime.now(timezone.utc)} - ) + result = {"leaderboards": leaderboards, "now": datetime.now(timezone.utc)} + + _cache["data"] = result + _cache["timestamp"] = now + + return http_success(result) def _get_query():