Files
wdm/build/db.py
T
bryan.hoffmann 36180448e1
Build und Push Docker Image / build-and-push (push) Failing after 49s
Initial commit
2026-08-20 12:24:38 +02:00

92 lines
3.0 KiB
Python

"""
Datenbank-Anbindung für die WDM-Performance-Anwendung.
Verwendet einen Thread-sicheren Connection-Pool. Beim Start wird mehrfach
versucht die Verbindung aufzubauen, da Postgres im Docker-Setup u.U. noch
nicht bereit ist, wenn der App-Container startet.
"""
import os
import time
import logging
from contextlib import contextmanager
import psycopg2
import psycopg2.extras
from psycopg2.pool import ThreadedConnectionPool
log = logging.getLogger("wdm-performance")
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
DB_HOST = os.environ.get("DB_HOST", "db")
DB_PORT = os.environ.get("DB_PORT", "5432")
DB_NAME = os.environ.get("DB_NAME", "wdm")
DB_USER = os.environ.get("DB_USER", "wdm")
DB_PASSWORD = os.environ.get("DB_PASSWORD", "wdm")
_pool = None
def init_pool(max_versuche=30, wartezeit_sekunden=2):
"""Baut den Connection-Pool auf. Versucht es mehrfach, falls die DB
beim Containerstart noch nicht erreichbar ist (kein harter Absturz)."""
global _pool
for versuch in range(1, max_versuche + 1):
try:
_pool = ThreadedConnectionPool(
minconn=1,
maxconn=10,
host=DB_HOST,
port=DB_PORT,
dbname=DB_NAME,
user=DB_USER,
password=DB_PASSWORD,
connect_timeout=5,
)
log.info("Datenbankverbindung zu %s:%s/%s hergestellt.", DB_HOST, DB_PORT, DB_NAME)
return
except psycopg2.OperationalError as exc:
if versuch == max_versuche:
log.error("Konnte nach %d Versuchen keine DB-Verbindung herstellen: %s", max_versuche, exc)
raise
log.warning("DB noch nicht erreichbar (Versuch %d/%d), warte %ds ...", versuch, max_versuche, wartezeit_sekunden)
time.sleep(wartezeit_sekunden)
@contextmanager
def get_conn():
"""Liefert eine Connection aus dem Pool. Bei Fehlern wird die
Connection verworfen statt sie kaputt zurückzugeben (Stabilität bei
Langzeitbetrieb)."""
if _pool is None:
init_pool()
conn = _pool.getconn()
try:
yield conn
conn.commit()
except Exception:
conn.rollback()
raise
finally:
_pool.putconn(conn)
@contextmanager
def get_cursor(dict_cursor=True):
with get_conn() as conn:
cursor_factory = psycopg2.extras.RealDictCursor if dict_cursor else None
cur = conn.cursor(cursor_factory=cursor_factory)
try:
yield cur
finally:
cur.close()
def migrate():
"""Leichte, idempotente Schema-Migrationen für bereits laufende
Installationen. `init.sql` wird nur beim allerersten Start (leeres
Datenvolume) ausgeführt - bestehende Installationen bekommen neue Spalten
stattdessen hier beim App-Start nachgerüstet."""
with get_cursor() as cur:
cur.execute('ALTER TABLE "Auftraege" ADD COLUMN IF NOT EXISTS "artikel" text')
log.info("Schema-Migration geprüft/angewendet.")