Moved database module to separate library
All checks were successful
Build & Publish to GHCR / build (push) Successful in 1m56s

This commit is contained in:
Florian 2025-11-06 17:27:10 +01:00
parent 18042a8ff5
commit b10a2cd8e1
3 changed files with 3 additions and 134 deletions

View File

@ -7,6 +7,7 @@ fastapi==0.118.2
feedparser==6.0.12 feedparser==6.0.12
h11==0.16.0 h11==0.16.0
idna==3.10 idna==3.10
lib-db-module @ git+https://git.gansejunge.com/notifier/lib-db-module.git@main
lib-secret-manager @ git+https://git.gansejunge.com/notifier/lib-secret-manager.git@main lib-secret-manager @ git+https://git.gansejunge.com/notifier/lib-secret-manager.git@main
lib-uvicorn-config @ git+https://git.gansejunge.com/notifier/lib-uvicorn-config.git@main lib-uvicorn-config @ git+https://git.gansejunge.com/notifier/lib-uvicorn-config.git@main
mysql-connector-python==9.4.0 mysql-connector-python==9.4.0

132
src/db.py
View File

@ -1,132 +0,0 @@
from mysql.connector import pooling, Error
import threading
from secret_manager import return_credentials
import os
import time
from simple_logger_handler import setup_logger
db_username = return_credentials("/etc/secrets/db_username")
db_password = return_credentials("/etc/secrets/db_password")
db_host = os.getenv("SERVICE_RR_DB_HOST","localhost")
db_database = os.getenv("SERVICE_RR_DB_HOST_DATABASE","app")
logger = setup_logger(__name__)
MAX_RETRIES = 5
RETRY_DELAY = 5
HEALTHCHECK_INTERVAL = 60
MYSQL_CONFIG = {
"host": db_host,
"user": db_username,
"password": db_password,
"database": db_database,
"connection_timeout": 10
}
_pool_lock = threading.Lock()
_connection_pool = None
_health_thread = None
_stop_healthcheck = threading.Event()
def create_connection_pool():
global _connection_pool
with _pool_lock:
if _connection_pool is not None:
logger.debug("[MySQL] Pool already exists, returning existing pool")
return
for attempt in range (1,MAX_RETRIES+1):
try:
logger.info(f"[MySQL] Attempt {attempt} to connect...")
pool = pooling.MySQLConnectionPool(
pool_name="royalRoadPool",
pool_size=5,
pool_reset_session=True,
**MYSQL_CONFIG
)
with _pool_lock:
_connection_pool = pool
logger.info("[MySQL] Pool created", extra={"pool_name": pool.pool_name})
return
except Error as e:
logger.warning(f"[MySQL] Attempt {attempt} failed: {e}")
if attempt < MAX_RETRIES:
logger.debug(f"[MySQL] Retrying in {RETRY_DELAY} seconds...")
time.sleep(RETRY_DELAY)
logger.critical(f"[MySQL] Failed to connect after {MAX_RETRIES} attempts.")
raise RuntimeError("MySQL pool initialization failed")
def close_connection_pool():
global _connection_pool
with _pool_lock:
if _connection_pool:
logger.debug(f"[MySQL] Closing pool: {_connection_pool}")
_connection_pool = None
logger.info("[MySQL] Connection pool closed")
_stop_healthcheck.set()
logger.debug("[MySQL] Healthcheck stop flag set")
def get_connection_pool():
global _connection_pool
with _pool_lock:
if _connection_pool is None:
logger.debug("[MySQL] No pool found, creating one")
create_connection_pool()
else:
logger.debug(f"[MySQL] Returning existing pool: {_connection_pool}")
return _connection_pool
def get_db():
pool = get_connection_pool()
logger.debug(f"[MySQL] Acquiring connection from pool: {pool}")
conn = pool.get_connection()
try:
conn.ping(reconnect=True, attempts=3, delay=1)
logger.debug("[MySQL] Connection alive")
except Error as e:
logger.warning(f"[MySQL] Connection dead, recreating pool: {e}")
create_connection_pool()
pool = get_connection_pool()
conn = pool.get_connection()
logger.debug("[MySQL] Reconnected successfully")
try:
yield conn
finally:
if conn and conn.is_connected():
conn.close()
logger.debug("[MySQL] Connection returned to pool")
def _pool_healthcheck():
while not _stop_healthcheck.is_set():
time.sleep(HEALTHCHECK_INTERVAL)
with _pool_lock:
pool = _connection_pool
if not pool:
logger.debug("[MySQL] Healthcheck skipped, pool not initialized")
continue
try:
conn = pool.get_connection()
conn.ping(reconnect=True, attempts=3, delay=1)
conn.close()
logger.debug(f"[MySQL] Pool healthcheck succeeded")
except Error as e:
logger.warning(f"[MySQL] Pool healthcheck failed: {e}")
create_connection_pool()
def start_healthcheck_thread():
global _health_thread
if _health_thread and _health_thread.is_alive():
logger.debug("[MySQL] Healthcheck thread already running")
return
_stop_healthcheck.clear()
_health_thread = threading.Thread(target=_pool_healthcheck, daemon=True)
_health_thread.start()
logger.info("[MySQL] Healthcheck thread started.")

View File

@ -1,7 +1,7 @@
from fastapi import FastAPI, Depends, HTTPException, Response, Request from fastapi import FastAPI, Depends, HTTPException, Response, Request
import uvicorn import uvicorn
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from db import get_db, create_connection_pool, close_connection_pool, start_healthcheck_thread from db_module import get_db, create_connection_pool, close_connection_pool, start_healthcheck_thread
from simple_logger_handler import setup_logger, LOG_LEVEL from simple_logger_handler import setup_logger, LOG_LEVEL
from feed_handler import grab_latest_chapter_information from feed_handler import grab_latest_chapter_information
from send_notification import send_notification from send_notification import send_notification
@ -18,7 +18,7 @@ async def lifespan(app: FastAPI):
logger.info("[App] Starting application...") logger.info("[App] Starting application...")
logger.info("[DB] Creating MySQL connection pool...") logger.info("[DB] Creating MySQL connection pool...")
create_connection_pool() create_connection_pool("royalRoadPool")
start_healthcheck_thread() start_healthcheck_thread()
logger.info("[DB] MySQL healthcheck thread started.") logger.info("[DB] MySQL healthcheck thread started.")