From 4a688e61c3e29327e5b59799035235399dc172e5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 13:39:09 +0000 Subject: [PATCH 1/2] Initial plan From 66b01a94fc41ae064f2fe044cd9cef2c87c1eb77 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 13:45:53 +0000 Subject: [PATCH 2/2] Fix PostgreSQL connection exhaustion with gevent workers - Set CONN_MAX_AGE default to 0 (close DB connections after each request) to prevent connection exhaustion when using gevent workers where each greenlet holds its own persistent connection - Fix buggy CONN_MAX_AGE env var logic that used Python `or` operator, which prevented explicitly setting CONN_MAX_AGE=0 - Remove non-functional POOL_OPTIONS (no pooling library installed) - Make gunicorn settings configurable via environment variables - Update .envs/.production/.django with new configuration docs Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com> --- .envs/.production/.django | 22 +++++++++------------- compose/production/django/start | 14 +++++++++++++- config/settings/production.py | 15 ++++++--------- 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/.envs/.production/.django b/.envs/.production/.django index f391c161b..a64655634 100644 --- a/.envs/.production/.django +++ b/.envs/.production/.django @@ -52,21 +52,17 @@ FETCH_DATA_TIMEOUT=10 # Exemplo: 10 segundos DB_CONNECT_TIMEOUT=10 -# --- Opções de Pool de Conexões --- -# (Aplicável se você estiver usando uma biblioteca de pool de conexões integrada ao Django, -# como django-db-connection-pool, que reconheça POOL_OPTIONS.) +# --- Gunicorn --- +# Number of gunicorn worker processes +# GUNICORN_WORKERS=3 -# Tamanho mínimo do pool de conexões -# Exemplo: 10 conexões -DB_POOL_SIZE=10 +# Max simultaneous connections per worker (gevent greenlets) +# GUNICORN_WORKER_CONNECTIONS=1000 -# Número máximo de conexões extras que o pool pode criar em caso de pico -# Exemplo: 20 conexões de "overflow" -DB_MAX_OVERFLOW=20 - -# Tempo máximo (em segundos) que uma conexão pode viver antes de ser reciclada -# Ajuda a evitar conexões "stale" ou "bad". Exemplo: 300 segundos (5 minutos) -DB_RECYCLE=300 +# Django CONN_MAX_AGE: seconds to keep DB connections alive (0 = close after each request). +# Use 0 (default) with gevent to prevent connection exhaustion +# (each greenlet holds its own connection when CONN_MAX_AGE > 0). +# CONN_MAX_AGE=0 # ---- profiling diff --git a/compose/production/django/start b/compose/production/django/start index d9da8eace..f774d835b 100755 --- a/compose/production/django/start +++ b/compose/production/django/start @@ -26,4 +26,16 @@ if compress_enabled; then # NOTE this command will fail if django-compressor is disabled python /app/manage.py compress fi -/usr/local/bin/gunicorn config.wsgi --bind 0.0.0.0:5000 --chdir=/app --timeout 1000 --workers 3 --worker-connections=1000 --worker-class=gevent + +GUNICORN_WORKERS=${GUNICORN_WORKERS:-3} +GUNICORN_WORKER_CONNECTIONS=${GUNICORN_WORKER_CONNECTIONS:-1000} +GUNICORN_WORKER_CLASS=${GUNICORN_WORKER_CLASS:-gevent} +GUNICORN_TIMEOUT=${GUNICORN_TIMEOUT:-1000} + +/usr/local/bin/gunicorn config.wsgi \ + --bind 0.0.0.0:5000 \ + --chdir=/app \ + --timeout "$GUNICORN_TIMEOUT" \ + --workers "$GUNICORN_WORKERS" \ + --worker-connections="$GUNICORN_WORKER_CONNECTIONS" \ + --worker-class="$GUNICORN_WORKER_CLASS" diff --git a/config/settings/production.py b/config/settings/production.py index 75fe9dc13..af84415a7 100755 --- a/config/settings/production.py +++ b/config/settings/production.py @@ -14,19 +14,16 @@ # ------------------------------------------------------------------------------ DATABASES["default"] = env.db("DATABASE_URL") # noqa F405 DATABASES["default"]["ATOMIC_REQUESTS"] = True # noqa F405 -DATABASES["default"]["CONN_MAX_AGE"] = env.int("CONN_MAX_AGE", default=0) or env.int("DJANGO_CONN_MAX_AGE", default=60) # noqa F405 +# CONN_MAX_AGE=0 closes DB connections after each request, which is recommended +# when using gevent workers to prevent connection exhaustion — each greenlet +# holds its own persistent connection when CONN_MAX_AGE > 0. +# Set CONN_MAX_AGE env var to a positive integer (e.g. 60) only if using a +# connection pooler like PgBouncer in front of PostgreSQL. +DATABASES["default"]["CONN_MAX_AGE"] = env.int("CONN_MAX_AGE", default=0) # noqa F405 DATABASES["default"]["CONN_HEALTH_CHECKS"] = env.bool('DJANGO_CONN_HEALTH_CHECKS', True) DATABASES["default"]["ENGINE"] = 'django_prometheus.db.backends.postgresql' -# Melhoria: Usando variáveis de ambiente para OPTIONS e POOL_OPTIONS com defaults DATABASES["default"]["OPTIONS"] = { "connect_timeout": env.int("DB_CONNECT_TIMEOUT", default=10), - # Adicione outras opções de conexão aqui se necessário -} -DATABASES["default"]["POOL_OPTIONS"] = { - 'POOL_SIZE': env.int("DB_POOL_SIZE", default=10), - 'MAX_OVERFLOW': env.int("DB_MAX_OVERFLOW", default=20), - 'RECYCLE': env.int("DB_RECYCLE", default=300), - # Adicione outras opções do pool aqui se necessário } # CACHES # ------------------------------------------------------------------------------