mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-04 19:21:51 +00:00
fix(api): close DB connections per request to stop ASGI replica connection leak (#11640)
This commit is contained in:
@@ -15,6 +15,10 @@ All notable changes to the **Prowler API** are documented in this file.
|
||||
- Gunicorn worker timeout raised from the 30s default to 120s, so long-running requests are no longer killed prematurely [(#11631)](https://github.com/prowler-cloud/prowler/pull/11631)
|
||||
- Sentry now drops ASGI's `RequestAborted` errors from health-check probe disconnects on `/health/live` [(#11632)](https://github.com/prowler-cloud/prowler/pull/11632)
|
||||
|
||||
### 🐞 Fixed
|
||||
|
||||
- Database connections no longer leak under the ASGI worker, which previously exhausted the read replica's connection slots and caused 500s on read endpoints [(#11640)](https://github.com/prowler-cloud/prowler/pull/11640)
|
||||
|
||||
### 🔐 Security
|
||||
|
||||
- `aiohttp` to 3.14.0 and `idna` to 3.15, patching known CVEs [(#11596)](https://github.com/prowler-cloud/prowler/pull/11596)
|
||||
|
||||
@@ -1,9 +1,35 @@
|
||||
import logging
|
||||
import time
|
||||
|
||||
from django.core.handlers.asgi import ASGIRequest
|
||||
from django.db import connections
|
||||
|
||||
from config.custom_logging import BackendLogger
|
||||
|
||||
|
||||
class CloseDBConnectionsMiddleware:
|
||||
"""
|
||||
Close request-scoped DB connections at the end of each ASGI request.
|
||||
|
||||
Under the ASGI worker, connections opened by sync views are not released
|
||||
by Django's normal request-boundary cleanup, so they accumulate idle until
|
||||
Postgres runs out of slots. Only ASGI requests are handled; the sync WSGI
|
||||
test client manages its own connections and must be left alone.
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
try:
|
||||
return self.get_response(request)
|
||||
finally:
|
||||
if isinstance(request, ASGIRequest):
|
||||
for conn in connections.all(initialized_only=True):
|
||||
if not conn.in_atomic_block:
|
||||
conn.close_if_unusable_or_obsolete()
|
||||
|
||||
|
||||
def extract_auth_info(request) -> dict:
|
||||
if getattr(request, "auth", None) is not None:
|
||||
tenant_id = request.auth.get("tenant_id", "N/A")
|
||||
|
||||
@@ -49,6 +49,7 @@ INSTALLED_APPS = [
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
"api.middleware.CloseDBConnectionsMiddleware",
|
||||
"django_guid.middleware.guid_middleware",
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
|
||||
Reference in New Issue
Block a user