Fehlerbehebung bei Verbindungslecks in FastAPI mit SQLAlchemy und PostgreSQLPython

Python-Programme
Guest
 Fehlerbehebung bei Verbindungslecks in FastAPI mit SQLAlchemy und PostgreSQL

Post by Guest »

Ich habe in meinem FastAPI-Projekt ein Verbindungsleckproblem. Die Verbindungen werden nicht ordnungsgemäß geschlossen und bleiben im Ruhezustand. Trotz meiner Versuche, Verbindungen sorgfältig zu verwalten, steigt die Anzahl der inaktiven Verbindungen weiter an, was schließlich zu Problemen führt, wenn das maximale Verbindungslimit erreicht ist.
Projektdetails:
  • Framework: FastAPI (v0.104.0)
  • Webserver: Uvicorn (v0.23.0) mit 24 Workern
  • Datenbankbibliothek: SQLAlchemy (v2.0.21)
  • Datenbank: PostgreSQL (über Docker)
PostgreSQL-Konfiguration in docker-compose.yml :

Code: Select all

postgres:
image: postgres:16
container_name: postgres_db
ports:
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 300s
timeout: 5s
retries: 3
Datenbankkonfiguration:

Code: Select all

DATABASE_URL = os.environ.get("DATABASE_URL")

engine = create_engine(
DATABASE_URL,
pool_size=24,  # Number of connections in the pool
max_overflow=12,  # Additional connections allowed beyond the pool size
pool_timeout=30,  # Wait time for a connection before timeout
pool_recycle=1800,  # Recycle connections after 1800 seconds (30 minutes)
pool_pre_ping=True,  # Ensure connections are valid before using
)
Datenbankmanager:

Code: Select all

class DatabaseManager:
_instance = None

def __new__(cls):
if not cls._instance:
cls._instance = super(DatabaseManager, cls).__new__(cls)
cls._instance.engine = engine
cls._instance.session_maker = scoped_session(
sessionmaker(autocommit=False, autoflush=False, bind=cls._instance.engine)
)
return cls._instance

def get_session(self):
session = self._instance.session_maker()
try:
yield session
finally:
session.close()
self._instance.session_maker.remove()
Login-Router:

Code: Select all

@user_router.post("/login")
def login_user(
request: Request,
login_data: LoginRequest,
usermanager=Depends(UserManager),
db_session: Session = Depends(DatabaseManager().get_session),
):
return usermanager.login_user(request, login_data, db_session)
Anmeldelogik:

Code: Select all

def login_user(self, request, login_data, db_session:  Session):
try:
user = self.authenticate_user(request, login_data, db_session)
if not user:
raise HTTPException(status_code=401, detail="Incorrect username or password")

if user.is_active:
tokens = self.create_tokens_for_user(user)
return tokens
else:
raise HTTPException(status_code=401, detail="User is inactive")

except HTTPException as e:
self.log_exception("login", e.status_code, e.detail, traceback.format_exc())
raise e

except Exception as e:
try:
self.log_exception("login retry", 500, str(e), traceback.format_exc())
db_session = next(DatabaseManager().get_session())
user = self.authenticate_user(request, login_data, db_session)
if user and user.is_active:
return self.create_tokens_for_user(user)
finally:
db_session.close()

raise HTTPException(status_code=500, detail="An error occurred while logging in")

Inaktive Verbindungen nehmen zu:

Ich überwache die PostgreSQL-Verbindungen mithilfe der folgenden Abfrage im Container:

Code: Select all

SELECT state, count(*)
FROM pg_stat_activity
GROUP BY state;
Ausgabe:

Code: Select all

state  | count
-------+-------
|     5
active |     1
idle   |    73
und die Anzahl der Leerlaufverbindungen steigt!
Obwohl ich maximal 24 Leerlaufverbindungen erwarte (aufgrund der Poolgröße), ist die Die Anzahl nimmt weiter zu und erreicht das maximale Verbindungslimit.
Noch bevor die Anzahl der inaktiven Verbindungen das Maximum erreicht, erhalte ich manchmal diese Fehlermeldung:

Code: Select all

sqlalchemy.exc.InvalidRequestError: This session is provisioning a new connection;
concurrent operations are not permitted
Und wenn das Maximum erreicht ist, erhalte ich Folgendes:

Code: Select all

psycopg2.OperationalError: connection to server at "postgres", port 5432 failed:
FATAL:  sorry, too many clients already
Beobachtetes Verhalten:
Selbst wenn die Anzahl der inaktiven Verbindungen das Maximum erreicht, scheint die Wiederholungslogik in der Funktion login_user ordnungsgemäß zu funktionieren. Ich stelle sicher, dass alle Sitzungen ordnungsgemäß geschlossen werden (z. B. mit „final“), wenn ich keine FastAPI-Abhängigkeitsinjektion verwende. Darüber hinaus verwende ich eine bereichsbezogene Sitzung, sodass eine Sitzung pro Thread verwaltet wird. Das Problem besteht jedoch weiterhin.
Ich stecke seit einer Woche bei diesem Problem fest und habe alles versucht, was mir einfiel, einschließlich der Suche nach Hilfe bei jedem KI-Tool, das ich finden konnte. Leider hat nichts funktioniert. Deshalb bin ich hierhergekommen und hoffe auf Hilfe durch nicht-künstliche Intelligenz. Für Ratschläge oder Erkenntnisse zur Lösung dieses Problems wären wir sehr dankbar.
Zusätzliche Informationen:
Ich habe einen Klon meiner App ausgeführt, ohne dass Anfragen bearbeitet wurden , und hier sind die Verbindungsstatistiken nach etwa 14 Stunden:

Code: Select all

p_db=# SELECT state, count(*)
FROM pg_stat_activity
GROUP BY state;
state  | count
--------+-------
|     5
active |     1
idle   |    24
(3 rows)
Dies ist aufgrund der bereichsbezogenen Sitzung mit einer Verbindung pro Thread sinnvoll.
Allerdings in meiner Haupt-App, wo die einzige PostgreSQL-bezogene Vorgang ist Anmeldung, hier sind die Statistiken:

Code: Select all

p_db=# SELECT state, count(*)
FROM pg_stat_activity
GROUP BY state;
state  | count
--------+-------
|     5
active |     1
idle   |    89
Ich verstehe nicht, warum die Anzahl der inaktiven Verbindungen in der Haupt-App ständig zunimmt!
Die Anzahl der inaktiven Verbindungen hat also das Maximum erreicht und ich erhalte jetzt Folgendes:
Ich habe es schon einmal gesagt, und ich sage es noch einmal – trotz dieser Fehlermeldung funktioniert meine Anmeldung (naja, der Wiederholungsversuch) immer noch. Wie?!
(psycopg2.OperationalError) Verbindung zum Server bei „postgres“ (172.21.0.3), Port 5432 fehlgeschlagen: FATAL: Entschuldigung, bereits zu viele Clients

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post