feat: add 5-minute TTL cache for PostgreSQL history queries
This commit is contained in:
+17
-1
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user