performance: db/aiohttp connection pooling
This commit is contained in:
@@ -24,9 +24,7 @@ import aiohttp
|
||||
from fastapi import FastAPI, Depends, HTTPException, Request
|
||||
from fastapi_throttle import RateLimiter
|
||||
from fastapi.responses import JSONResponse
|
||||
import redis
|
||||
|
||||
from lyric_search.sources import private
|
||||
from auth.deps import get_current_user
|
||||
from dotenv import load_dotenv
|
||||
|
||||
@@ -72,10 +70,10 @@ class Lighting:
|
||||
self.util = util
|
||||
self.constants = constants
|
||||
|
||||
# Redis for state persistence
|
||||
self.redis_client = redis.Redis(
|
||||
password=private.REDIS_PW, decode_responses=True
|
||||
)
|
||||
# Redis for state persistence - use shared sync client
|
||||
import shared
|
||||
|
||||
self.redis_client = shared.get_redis_sync_client(decode_responses=True)
|
||||
self.lighting_key = "lighting:state"
|
||||
|
||||
# Cync configuration from environment
|
||||
|
||||
@@ -686,23 +686,23 @@ class Radio(FastAPI):
|
||||
|
||||
async def _send_lrc_to_client(self, websocket: WebSocket, station: str, track_data: dict):
|
||||
"""Send cached LRC data to a specific client asynchronously. Only sends if LRC exists in cache."""
|
||||
logging.info(f"[LRC Send] Checking cached LRC for station {station}")
|
||||
logging.info(f"[LRC Send] Current track: {track_data.get('artist', 'Unknown')} - {track_data.get('song', 'Unknown')}")
|
||||
logging.debug(f"[LRC Send] Checking cached LRC for station {station}")
|
||||
logging.debug(f"[LRC Send] Current track: {track_data.get('artist', 'Unknown')} - {track_data.get('song', 'Unknown')}")
|
||||
try:
|
||||
# Only send if LRC is in cache
|
||||
cached_lrc = self.lrc_cache.get(station)
|
||||
logging.info(f"[LRC Send] Cache status for station {station}: {'Found' if cached_lrc else 'Not found'}")
|
||||
logging.debug(f"[LRC Send] Cache status for station {station}: {'Found' if cached_lrc else 'Not found'}")
|
||||
if cached_lrc:
|
||||
logging.info("[LRC Send] Sending cached LRC to client")
|
||||
logging.debug("[LRC Send] Sending cached LRC to client")
|
||||
lrc_data: dict = {
|
||||
"type": "lrc",
|
||||
"data": cached_lrc,
|
||||
"source": "Cache"
|
||||
}
|
||||
await websocket.send_text(json.dumps(lrc_data))
|
||||
logging.info("[LRC Send] Successfully sent cached LRC to client")
|
||||
logging.debug("[LRC Send] Successfully sent cached LRC to client")
|
||||
else:
|
||||
logging.info(f"[LRC Send] No cached LRC available for station {station}")
|
||||
logging.debug(f"[LRC Send] No cached LRC available for station {station}")
|
||||
except Exception as e:
|
||||
logging.error(f"[LRC Send] Failed to send cached LRC to client: {e}")
|
||||
logging.error(f"[LRC Send] Error details: {traceback.format_exc()}")
|
||||
@@ -711,34 +711,34 @@ class Radio(FastAPI):
|
||||
"""Send cached LRC data to a specific client asynchronously. Only sends if valid LRC exists in cache."""
|
||||
try:
|
||||
track_info = f"{track_data.get('artist', 'Unknown')} - {track_data.get('song', 'Unknown')}"
|
||||
logging.info(f"[LRC Send {id(websocket)}] Starting LRC send for {track_info}")
|
||||
logging.info(f"[LRC Send {id(websocket)}] Cache keys before lock: {list(self.lrc_cache.keys())}")
|
||||
logging.debug(f"[LRC Send {id(websocket)}] Starting LRC send for {track_info}")
|
||||
logging.debug(f"[LRC Send {id(websocket)}] Cache keys before lock: {list(self.lrc_cache.keys())}")
|
||||
|
||||
# Get cached LRC with lock to ensure consistency
|
||||
async with self.lrc_cache_locks[station]:
|
||||
logging.info(f"[LRC Send {id(websocket)}] Got cache lock")
|
||||
logging.debug(f"[LRC Send {id(websocket)}] Got cache lock")
|
||||
cached_lrc = self.lrc_cache.get(station)
|
||||
logging.info(f"[LRC Send {id(websocket)}] Cache keys during lock: {list(self.lrc_cache.keys())}")
|
||||
logging.info(f"[LRC Send {id(websocket)}] Cache entry length: {len(cached_lrc) if cached_lrc else 0}")
|
||||
logging.debug(f"[LRC Send {id(websocket)}] Cache keys during lock: {list(self.lrc_cache.keys())}")
|
||||
logging.debug(f"[LRC Send {id(websocket)}] Cache entry length: {len(cached_lrc) if cached_lrc else 0}")
|
||||
|
||||
# Only send if we have actual lyrics
|
||||
if cached_lrc:
|
||||
logging.info(f"[LRC Send {id(websocket)}] Preparing to send {len(cached_lrc)} bytes of LRC")
|
||||
logging.debug(f"[LRC Send {id(websocket)}] Preparing to send {len(cached_lrc)} bytes of LRC")
|
||||
lrc_data: dict = {
|
||||
"type": "lrc",
|
||||
"data": cached_lrc,
|
||||
"source": "Cache"
|
||||
}
|
||||
await websocket.send_text(json.dumps(lrc_data))
|
||||
logging.info(f"[LRC Send {id(websocket)}] Successfully sent LRC")
|
||||
logging.debug(f"[LRC Send {id(websocket)}] Successfully sent LRC")
|
||||
else:
|
||||
logging.info(f"[LRC Send {id(websocket)}] No LRC in cache")
|
||||
logging.debug(f"[LRC Send {id(websocket)}] No LRC in cache")
|
||||
# If we have no cache entry, let's check if a fetch is needed
|
||||
async with self.lrc_cache_locks[station]:
|
||||
logging.info(f"[LRC Send {id(websocket)}] Checking if fetch needed")
|
||||
logging.debug(f"[LRC Send {id(websocket)}] Checking if fetch needed")
|
||||
# Only attempt fetch if we're the first to notice missing lyrics
|
||||
if station not in self.lrc_cache:
|
||||
logging.info(f"[LRC Send {id(websocket)}] Initiating LRC fetch")
|
||||
logging.debug(f"[LRC Send {id(websocket)}] Initiating LRC fetch")
|
||||
lrc, source = await self._fetch_and_cache_lrc(station, track_data)
|
||||
if lrc:
|
||||
self.lrc_cache[station] = lrc
|
||||
@@ -748,7 +748,7 @@ class Radio(FastAPI):
|
||||
"source": source
|
||||
}
|
||||
await websocket.send_text(json.dumps(lrc_data))
|
||||
logging.info(f"[LRC Send {id(websocket)}] Sent newly fetched LRC")
|
||||
logging.debug(f"[LRC Send {id(websocket)}] Sent newly fetched LRC")
|
||||
except Exception as e:
|
||||
logging.error(f"[LRC Send {id(websocket)}] Failed: {e}")
|
||||
logging.error(f"[LRC Send {id(websocket)}] Error details: {traceback.format_exc()}")
|
||||
@@ -761,25 +761,25 @@ class Radio(FastAPI):
|
||||
duration: Optional[int] = track_data.get("duration")
|
||||
|
||||
if not (artist and title):
|
||||
logging.info("[LRC] Missing artist or title, skipping fetch")
|
||||
logging.debug("[LRC] Missing artist or title, skipping fetch")
|
||||
return None, "None"
|
||||
|
||||
logging.info(f"[LRC] Starting fetch for {station}: {artist} - {title}")
|
||||
logging.debug(f"[LRC] Starting fetch for {station}: {artist} - {title}")
|
||||
|
||||
# Try LRCLib first with timeout
|
||||
try:
|
||||
async with asyncio.timeout(10.0): # 10 second timeout
|
||||
logging.info("[LRC] Trying LRCLib")
|
||||
logging.debug("[LRC] Trying LRCLib")
|
||||
lrclib_result = await self.lrclib.search(artist, title, plain=False, raw=True)
|
||||
if lrclib_result and lrclib_result.lyrics and isinstance(lrclib_result.lyrics, str):
|
||||
logging.info("[LRC] Found from LRCLib")
|
||||
logging.debug("[LRC] Found from LRCLib")
|
||||
return lrclib_result.lyrics, "LRCLib"
|
||||
except asyncio.TimeoutError:
|
||||
logging.warning("[LRC] LRCLib fetch timed out")
|
||||
except Exception as e:
|
||||
logging.error(f"[LRC] LRCLib fetch error: {e}")
|
||||
|
||||
logging.info("[LRC] LRCLib fetch completed without results")
|
||||
logging.debug("[LRC] LRCLib fetch completed without results")
|
||||
|
||||
# Try SR as fallback with timeout
|
||||
try:
|
||||
@@ -788,14 +788,14 @@ class Radio(FastAPI):
|
||||
artist, title, duration=duration
|
||||
)
|
||||
if lrc:
|
||||
logging.info("[LRC] Found from SR")
|
||||
logging.debug("[LRC] Found from SR")
|
||||
return lrc, "SR"
|
||||
except asyncio.TimeoutError:
|
||||
logging.warning("[LRC] SR fetch timed out")
|
||||
except Exception as e:
|
||||
logging.error(f"[LRC] SR fetch error: {e}")
|
||||
|
||||
logging.info("[LRC] No lyrics found from any source")
|
||||
logging.debug("[LRC] No lyrics found from any source")
|
||||
return None, "None"
|
||||
except Exception as e:
|
||||
logging.error(f"[LRC] Error fetching lyrics: {e}")
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import os
|
||||
import random
|
||||
from typing import LiteralString, Optional, Union
|
||||
import aiosqlite as sqlite3
|
||||
from fastapi import FastAPI, Depends
|
||||
from fastapi_throttle import RateLimiter
|
||||
from fastapi.responses import JSONResponse
|
||||
from .constructors import RandMsgRequest
|
||||
import shared # Use shared SQLite pool
|
||||
|
||||
|
||||
class RandMsg(FastAPI):
|
||||
@@ -103,11 +103,11 @@ class RandMsg(FastAPI):
|
||||
}
|
||||
)
|
||||
|
||||
async with sqlite3.connect(database=randmsg_db_path, timeout=1) as _db:
|
||||
async with await _db.execute(db_query) as _cursor:
|
||||
if not isinstance(_cursor, sqlite3.Cursor):
|
||||
return JSONResponse(content={"err": True})
|
||||
result: Optional[sqlite3.Row] = await _cursor.fetchone()
|
||||
# Use shared SQLite pool for connection reuse
|
||||
sqlite_pool = shared.get_sqlite_pool()
|
||||
async with sqlite_pool.connection(randmsg_db_path, timeout=1) as _db:
|
||||
async with _db.execute(db_query) as _cursor:
|
||||
result = await _cursor.fetchone()
|
||||
if not result:
|
||||
return JSONResponse(content={"err": True})
|
||||
(result_id, result_msg) = result
|
||||
|
||||
Reference in New Issue
Block a user