This commit is contained in:
+91
@@ -0,0 +1,91 @@
|
||||
"""
|
||||
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.")
|
||||
Reference in New Issue
Block a user