Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .env_sample
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ DB_NAME=postgres
DB_USERNAME=postgres
DB_PASSWORD=postgres
DB_PORT=5432
DB_CONN_MAX_AGE=0
DB_CONN_HEALTH_CHECKS=True

# Gunicorn tuning
GUNICORN_WORKERS=4
GUNICORN_MAX_REQUESTS=2000
GUNICORN_MAX_REQUESTS_JITTER=200
GUNICORN_TIMEOUT=120
GUNICORN_KEEPALIVE=5

DJANGO_SETTINGS_MODULE=settings.develop
ALLOWED_HOSTS=localhost,example.com
Expand Down
29 changes: 29 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,35 @@ services:
max-size: "20m"
max-file: "5"

#----------------------------------------------------------------------------------------------------
# pgbouncer for connection pooling to Postgres, configured to use SCRAM-SHA-256 authentication and ignore the
#----------------------------------------------------------------------------------------------------
pgbouncer:
image: edoburu/pgbouncer:latest
container_name: pgbouncer
env_file: .env
environment:
- DB_USER=${DB_USERNAME}
- DB_PASSWORD=${DB_PASSWORD}
- DB_HOST=db
- DB_PORT=5432
- DB_NAME=${DB_NAME}
- POOL_MODE=transaction
- MAX_CLIENT_CONN=2000
- DEFAULT_POOL_SIZE=80
- RESERVE_POOL_SIZE=20
- AUTH_TYPE=scram-sha-256
- IGNORE_STARTUP_PARAMETERS=extra_float_digits
depends_on:
- db
restart: unless-stopped
ports:
- 6432:5432
logging:
options:
max-size: "20m"
max-file: "5"

#----------------------------------------------------------------------------------------------------
# Rabbitmq & Flower monitoring tool
#----------------------------------------------------------------------------------------------------
Expand Down
7 changes: 6 additions & 1 deletion src/gunicorn_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,12 @@ def load(self):
"errorlog": "-",
"worker_class": "uvicorn.workers.UvicornWorker",
"logger_class": StubbedGunicornLogger,
"capture_output": 'true'
"capture_output": 'true',
# Recycle workers periodically to release DB connections and avoid leaks
"max_requests": int(os.environ.get("GUNICORN_MAX_REQUESTS", "2000")),
"max_requests_jitter": int(os.environ.get("GUNICORN_MAX_REQUESTS_JITTER", "200")),
"timeout": int(os.environ.get("GUNICORN_TIMEOUT", "120")),
"keepalive": int(os.environ.get("GUNICORN_KEEPALIVE", "5"))
}

StandaloneApplication(app, options).run()
4 changes: 4 additions & 0 deletions src/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@
}
}

# With ASGI workers and PgBouncer, keep Django DB connections short-lived.
DATABASES['default']['CONN_MAX_AGE'] = int(os.environ.get('DB_CONN_MAX_AGE', '0'))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like we also need to test cases in which a long term connection to the database is necessary, like mass downloading submissions as an organizer ?

DATABASES['default']['CONN_HEALTH_CHECKS'] = os.environ.get('DB_CONN_HEALTH_CHECKS', 'True').lower() == 'true'

# TODO: Pull this, leaving in case django-oauth-toolkit problems
# # =============================================================================
# # SSL
Expand Down