All checks were successful
Deploy Development / deploy (push) Successful in 34s
Test Suite / pytest-backend (push) Successful in 10s
Test Suite / lint-backend (push) Successful in 2s
Test Suite / compose-smoke (push) Has been skipped
Test Suite / k6 /api/health Baseline (push) Successful in 18s
Test Suite / playwright-smoke (push) Successful in 12s
Co-authored-by: Cursor <cursoragent@cursor.com>
76 lines
2.1 KiB
Python
76 lines
2.1 KiB
Python
"""PostgreSQL connection helpers."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import time
|
|
|
|
import psycopg2
|
|
from psycopg2.extensions import connection
|
|
|
|
|
|
def db_params() -> dict[str, str]:
|
|
return {
|
|
"host": os.getenv("DB_HOST", "localhost"),
|
|
"port": os.getenv("DB_PORT", "5432"),
|
|
"dbname": os.getenv("DB_NAME", "kairo_dev"),
|
|
"user": os.getenv("DB_USER", "kairo_dev"),
|
|
"password": os.getenv("DB_PASSWORD", "dev_password"),
|
|
}
|
|
|
|
|
|
def get_connection() -> connection:
|
|
p = db_params()
|
|
return psycopg2.connect(
|
|
host=p["host"],
|
|
port=p["port"],
|
|
database=p["dbname"],
|
|
user=p["user"],
|
|
password=p["password"],
|
|
)
|
|
|
|
|
|
def is_retryable_db_error(exc: Exception) -> bool:
|
|
"""Nur temporäre Verbindungsprobleme erneut versuchen (nicht Auth/Config)."""
|
|
msg = str(exc).lower()
|
|
if "password authentication failed" in msg:
|
|
return False
|
|
if "does not exist" in msg and any(token in msg for token in ("role", "database")):
|
|
return False
|
|
if "no pg_hba.conf entry" in msg:
|
|
return False
|
|
return True
|
|
|
|
|
|
def connect_with_retry(max_retries: int = 30, sleep_seconds: float = 2.0) -> connection:
|
|
p = db_params()
|
|
last_exc: Exception | None = None
|
|
for attempt in range(max_retries):
|
|
try:
|
|
conn = get_connection()
|
|
conn.autocommit = False
|
|
print(f"[OK] Connected to database: {p['dbname']}")
|
|
return conn
|
|
except psycopg2.OperationalError as exc:
|
|
last_exc = exc
|
|
if not is_retryable_db_error(exc):
|
|
raise
|
|
if attempt >= max_retries - 1:
|
|
raise
|
|
print(f"Waiting for database... ({attempt + 1}/{max_retries})")
|
|
time.sleep(sleep_seconds)
|
|
if last_exc:
|
|
raise last_exc
|
|
raise RuntimeError("database connection failed")
|
|
|
|
|
|
def check_db() -> bool:
|
|
conn = get_connection()
|
|
try:
|
|
with conn.cursor() as cur:
|
|
cur.execute("SELECT 1")
|
|
cur.fetchone()
|
|
return True
|
|
finally:
|
|
conn.close()
|