Projektdetails:
- Framework: FastAPI (v0.104.0)
- Webserver: Uvicorn (v0.23.0) mit 24 Workern
- Datenbankbibliothek: SQLAlchemy (v2.0.21)
- Datenbank: PostgreSQL (über Docker)
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
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
)
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()
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)
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")
Ich überwache die PostgreSQL-Verbindungen mithilfe der folgenden Abfrage im Container:
Code: Select all
SELECT state, count(*)
FROM pg_stat_activity
GROUP BY state;
Code: Select all
state | count
-------+-------
| 5
active | 1
idle | 73
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
Code: Select all
psycopg2.OperationalError: connection to server at "postgres", port 5432 failed:
FATAL: sorry, too many clients already
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)
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
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