feat: add 5-minute TTL cache for PostgreSQL history queries

This commit is contained in:
2026-08-09 17:15:47 +07:00
parent c00a26402a
commit 2fe1dcf4da
+17 -1
View File
@@ -5,8 +5,10 @@ FastAPI web interface for water monitoring system
import asyncio import asyncio
import os import os
import time
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from datetime import datetime, timedelta from datetime import datetime, timedelta
from threading import Lock
from typing import Any, Dict, List from typing import Any, Dict, List
import requests import requests
@@ -34,6 +36,11 @@ from .water_scraper_v3 import EnhancedWaterMonitorScraper
logger = get_logger(__name__) logger = get_logger(__name__)
# Simple thread-safe TTL cache for PostgreSQL history queries
HISTORY_CACHE: Dict[str, tuple] = {}
HISTORY_CACHE_LOCK = Lock()
HISTORY_TTL = 300 # 5 minutes
# Dashboard HTML is loaded once at import from src/static/dashboard.html. # Dashboard HTML is loaded once at import from src/static/dashboard.html.
_DASHBOARD_HTML_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "static", "dashboard.html") _DASHBOARD_HTML_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "static", "dashboard.html")
try: try:
@@ -448,19 +455,28 @@ async def get_postgres_history(
limit: int = Query(2000, ge=1, le=5000), limit: int = Query(2000, ge=1, le=5000),
): ):
"""Get historical measurements for a station from PostgreSQL.""" """Get historical measurements for a station from PostgreSQL."""
cache_key = f"{station_code}:{hours}:{limit}"
now = time.monotonic()
with HISTORY_CACHE_LOCK:
cached = HISTORY_CACHE.get(cache_key)
if cached and now - cached[0] < HISTORY_TTL:
return cached[1]
try: try:
db_config = Config.get_database_config() db_config = Config.get_database_config()
if db_config["type"] != "postgresql": if db_config["type"] != "postgresql":
raise RuntimeError("PostgreSQL is not configured") raise RuntimeError("PostgreSQL is not configured")
history = PostgresHistory(db_config["connection_string"]) history = PostgresHistory(db_config["connection_string"])
end_time = datetime.now() end_time = datetime.now()
return await asyncio.to_thread( data = await asyncio.to_thread(
history.station_history, history.station_history,
station_code, station_code,
end_time - timedelta(hours=hours), end_time - timedelta(hours=hours),
end_time, end_time,
limit, limit,
) )
with HISTORY_CACHE_LOCK:
HISTORY_CACHE[cache_key] = (now, data)
return data
except RuntimeError as error: except RuntimeError as error:
raise HTTPException(status_code=503, detail=str(error)) raise HTTPException(status_code=503, detail=str(error))
except Exception as error: except Exception as error: