perf: persistance du cache concerts en SQLite (survit aux redémarrages)
Docker / docker (push) Successful in 3m31s
Docker / docker (push) Successful in 3m31s
Le cache en mémoire (6h TTL) est désormais sauvegardé dans concerts_cache. Au redémarrage, si le cache SQLite est récent, aucune requête réseau n'est faite. Le bouton Rafraîchir vide aussi le cache SQLite pour forcer un re-scrape. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+59
@@ -1,4 +1,5 @@
|
||||
import re
|
||||
import sqlite3
|
||||
import time
|
||||
import logging
|
||||
import asyncio
|
||||
@@ -11,8 +12,51 @@ import tmdb as _tmdb
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
CACHE_TTL = 6 * 3600
|
||||
DB_PATH = "arte_dl.db"
|
||||
_cache: dict = {"data": [], "ts": 0}
|
||||
|
||||
|
||||
def _db():
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
conn.row_factory = sqlite3.Row
|
||||
return conn
|
||||
|
||||
|
||||
def _init_concerts_cache_table():
|
||||
with _db() as conn:
|
||||
conn.execute("""
|
||||
CREATE TABLE IF NOT EXISTS concerts_cache (
|
||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||
data TEXT NOT NULL,
|
||||
ts REAL NOT NULL
|
||||
)
|
||||
""")
|
||||
|
||||
|
||||
_init_concerts_cache_table()
|
||||
|
||||
|
||||
def _load_db_cache() -> tuple[list, float]:
|
||||
try:
|
||||
with _db() as conn:
|
||||
row = conn.execute("SELECT data, ts FROM concerts_cache WHERE id=1").fetchone()
|
||||
if row:
|
||||
return json.loads(row["data"]), row["ts"]
|
||||
except Exception:
|
||||
pass
|
||||
return [], 0.0
|
||||
|
||||
|
||||
def _save_db_cache(data: list, ts: float):
|
||||
try:
|
||||
with _db() as conn:
|
||||
conn.execute(
|
||||
"INSERT OR REPLACE INTO concerts_cache (id, data, ts) VALUES (1, ?, ?)",
|
||||
(json.dumps(data), ts),
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning("Failed to save concerts cache: %s", e)
|
||||
|
||||
PLAYER_API = "https://api.arte.tv/api/player/v2/config/fr/{pid}"
|
||||
SEARCH_URL = "https://www.arte.tv/fr/search/?q={q}"
|
||||
|
||||
@@ -157,11 +201,21 @@ async def get_all_concerts() -> list[dict]:
|
||||
now = time.time()
|
||||
if _cache["data"] and now - _cache["ts"] < CACHE_TTL:
|
||||
return _cache["data"]
|
||||
|
||||
# Try SQLite cache before hitting the network
|
||||
db_data, db_ts = _load_db_cache()
|
||||
if db_data and now - db_ts < CACHE_TTL:
|
||||
logger.info("Concerts cache loaded from DB (%d concerts)", len(db_data))
|
||||
_cache["data"] = db_data
|
||||
_cache["ts"] = db_ts
|
||||
return _cache["data"]
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
data = await loop.run_in_executor(None, _fetch_all_sync)
|
||||
if data:
|
||||
_cache["data"] = data
|
||||
_cache["ts"] = now
|
||||
_save_db_cache(data, now)
|
||||
return _cache["data"]
|
||||
|
||||
|
||||
@@ -208,5 +262,10 @@ async def fetch_concerts(page: int = 1, search: str = "", page_size: int = 24, c
|
||||
|
||||
async def invalidate_cache() -> int:
|
||||
_cache["ts"] = 0
|
||||
try:
|
||||
with _db() as conn:
|
||||
conn.execute("DELETE FROM concerts_cache")
|
||||
except Exception:
|
||||
pass
|
||||
data = await get_all_concerts()
|
||||
return len(data)
|
||||
|
||||
Reference in New Issue
Block a user