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
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:
parent
88792eeb47
commit
f8b3ae3011
|
|
@ -1,6 +1,9 @@
|
||||||
"""PostgreSQL connection helpers."""
|
"""PostgreSQL connection helpers."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
import psycopg2
|
import psycopg2
|
||||||
from psycopg2.extensions import connection
|
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:
|
def check_db() -> bool:
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,12 @@ import re
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
|
||||||
from typing import List, Tuple
|
from typing import List, Tuple
|
||||||
|
|
||||||
import psycopg2
|
import psycopg2
|
||||||
import sqlparse
|
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+)")
|
_LEADING_DIGITS = re.compile(r"^(\d+)")
|
||||||
|
|
||||||
|
|
@ -135,21 +134,6 @@ def run_migration(conn, migration_name: str, filepath: str) -> bool:
|
||||||
return False
|
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:
|
def migrations_directory() -> str:
|
||||||
docker_path = "/app/migrations"
|
docker_path = "/app/migrations"
|
||||||
if os.path.isdir(docker_path):
|
if os.path.isdir(docker_path):
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ from typing import Callable, List, Optional, Tuple
|
||||||
import psycopg2
|
import psycopg2
|
||||||
import sqlparse
|
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+)_(.+)$")
|
_SEED_PREFIX = re.compile(r"^seed_(\d+)_(.+)$")
|
||||||
_DEV_MARKER = ".dev."
|
_DEV_MARKER = ".dev."
|
||||||
|
|
@ -152,21 +152,6 @@ def run_python_seed(filepath: str) -> None:
|
||||||
run_fn()
|
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]:
|
def run_seed(conn, seed_name: str, filepath: str, kind: str) -> tuple[bool, object]:
|
||||||
print(f"Running seed: {seed_name}")
|
print(f"Running seed: {seed_name}")
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -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
|
## Manuelles Deploy
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user