DB-Verbindung: bei Auth-Fehlern sofort abbrechen, Doku zum Volume-Wechsel.
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>
This commit is contained in:
Lars 2026-07-04 22:46:12 +02:00
parent 88792eeb47
commit f8b3ae3011
4 changed files with 55 additions and 33 deletions

View File

@ -1,6 +1,9 @@
"""PostgreSQL connection helpers."""
from __future__ import annotations
import os
import time
import psycopg2
from psycopg2.extensions import connection
@ -27,6 +30,40 @@ def get_connection() -> connection:
)
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:

View File

@ -8,13 +8,12 @@ import re
import shutil
import subprocess
import sys
import time
from typing import List, Tuple
import psycopg2
import sqlparse
from db import db_params, get_connection
from db import check_db, connect_with_retry, db_params, get_connection
_LEADING_DIGITS = re.compile(r"^(\d+)")
@ -135,21 +134,6 @@ def run_migration(conn, migration_name: str, filepath: str) -> bool:
return False
def connect_with_retry(max_retries: int = 30):
p = db_params()
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:
if attempt >= max_retries - 1:
raise
print(f"Waiting for database... ({attempt + 1}/{max_retries})")
time.sleep(2)
def migrations_directory() -> str:
docker_path = "/app/migrations"
if os.path.isdir(docker_path):

View File

@ -14,7 +14,7 @@ from typing import Callable, List, Optional, Tuple
import psycopg2
import sqlparse
from db import db_params, get_connection
from db import connect_with_retry, db_params, get_connection
_SEED_PREFIX = re.compile(r"^seed_(\d+)_(.+)$")
_DEV_MARKER = ".dev."
@ -152,21 +152,6 @@ def run_python_seed(filepath: str) -> None:
run_fn()
def connect_with_retry(max_retries: int = 30):
p = db_params()
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:
if attempt >= max_retries - 1:
raise
print(f"Waiting for database... ({attempt + 1}/{max_retries})")
time.sleep(2)
def run_seed(conn, seed_name: str, filepath: str, kind: str) -> tuple[bool, object]:
print(f"Running seed: {seed_name}")
try:

View File

@ -75,6 +75,22 @@ Aktuell keine Medien-Speicherung. Bei Bedarf: NAS-Mount + `docker-compose.overri
---
## Dev-Datenbank wechseln
PostgreSQL im Compose-Stack initialisiert User/Passwort **nur beim ersten Start** des Volumes (`dev-kairo-db-data`).
Wenn du `DB_NAME`, `DB_USER` oder `DB_PASSWORD` in `.env` änderst, muss das Volume neu angelegt werden:
```bash
cd /home/lars/docker/kairo-dev
docker compose -f docker-compose.dev-env.yml down -v
docker compose -f docker-compose.dev-env.yml up -d --wait
curl -sf http://localhost:8097/api/health
```
Danach laufen Migrationen und Dev-Seeds automatisch (u. a. `lars@stommer.com`).
---
## Manuelles Deploy
```bash