Before a Django launch, what do the defaults already do for CONN_MAX_AGE and the cached template loader, and when do you change them?
answer
- per-request overhead you can remove
- a connection per request by default
- loaders you did not specify
- explicit loaders lose the wrapper
basics
~20 sCONN_MAX_AGE defaults to 0, opening a database connection per request; under WSGI a positive value with CONN_HEALTH_CHECKS reuses it. The cached template loader is already on unless OPTIONS['loaders'] is set, in which case you wrap the loaders yourself.
solid answer
~40 s`CONN_MAX_AGE` defaults to `0`, so Django closes the database connection at the end of every request and the next request reconnects. Under WSGI, setting it to a positive number of seconds keeps each thread's connection for reuse, and `CONN_HEALTH_CHECKS = True` replaces connections the server has dropped; budget one connection per worker thread. Under ASGI the docs say to keep persistent connections off and use pooling, such as the PostgreSQL `pool` option from Django 5.1. The cached template loader needs no action in most projects: since Django 4.1 it wraps the default loaders whenever `OPTIONS['loaders']` is not specified, in development and production alike. If you do list loaders explicitly — for a custom loader, say — nothing is cached until you wrap them in `django.template.loaders.cached.Loader` yourself, and `APP_DIRS` must then be removed.
code
python · 27 linesTEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'OPTIONS': {
'loaders': [
(
'django.template.loaders.cached.Loader',
[
'myproject.loaders.TenantLoader',
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
],
),
],
},
},
]
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'shop',
'CONN_MAX_AGE': 60,
'CONN_HEALTH_CHECKS': True,
},
}go deeper
Know that CONN_MAX_AGE controls database connection reuse and that templates are cached by default in current Django.
Explain CONN_MAX_AGE's default of 0, what a positive value and CONN_HEALTH_CHECKS do, and when the cached loader is and is not applied automatically.
Choose connection reuse or pooling from the serving mode and the database's connection budget, and catch explicit loader lists that silently lost caching.
Standardise connection strategy across services sharing a database, balancing per-request latency against connection slots and operational complexity.
## Two settings that cost something on every request Most of a pre-launch checklist is about safety. Two items are about per-request overhead: opening database connections and loading templates from disk. Django's defaults handle one well and the other conservatively, and neither is reported by `check --deploy`. ## CONN_MAX_AGE: connection reuse **`CONN_MAX_AGE`** is set per database alias in `DATABASES` and is the maximum lifetime of a connection in seconds. - **Default `0`:** the connection is closed at the end of each request. Every request that queries the database pays for a new connection, including authentication with the database server. - **Positive value:** the connection is kept and reused by later requests on the same thread until it reaches that age. - **`None`:** connections are kept indefinitely. When enabling reuse, also consider **`CONN_HEALTH_CHECKS = True`** (default `False`): Django checks a reused connection once per request before using it, so a connection closed by a database restart is replaced instead of failing a request. Two constraints come with it: 1. **Connections are per thread.** The database must accept at least as many connections as you run worker threads across all processes and instances, now held even while idle. 2. **ASGI is different.** Django's database docs say persistent connections should be disabled under ASGI; use the backend's pooling instead — for PostgreSQL the `pool` option in `OPTIONS`, added in Django 5.1, which refuses to run with a non-zero `CONN_MAX_AGE`. ## The cached template loader A **template loader** finds and compiles templates. The **cached loader** (`django.template.loaders.cached.Loader`) wraps other loaders and keeps each compiled template in memory after the first use, avoiding disk reads and recompilation on every render. | Configuration | Is caching on? | |---|---| | `APP_DIRS = True`, no `OPTIONS['loaders']` | yes: Django wraps the filesystem and app-directories loaders in the cached loader | | no `OPTIONS['loaders']` and `APP_DIRS` false | yes: the filesystem loader is wrapped | | explicit `OPTIONS['loaders']` without the cached wrapper | **no**: every render goes back to the listed loaders | | explicit `OPTIONS['loaders']` wrapped in the cached loader | yes | Since **Django 4.1** this default applies **in development too**; during `runserver` the autoreloader resets the cache when templates change, so edits still show up. Before 4.1, the cached loader was only enabled automatically when `DEBUG` was false, which is why older checklists tell you to switch it on for production. When you list loaders explicitly, `APP_DIRS` must not also be set — Django raises `ImproperlyConfigured` — so include the app-directories loader in the list yourself. ## Deciding at launch - WSGI deployment with a nearby database and a connection budget to spare: set `CONN_MAX_AGE` to a modest number of seconds and enable `CONN_HEALTH_CHECKS`. - Database far away or expensive to connect to, but connection slots tight: prefer a pool or an external pooler over many idle persistent connections. - ASGI deployment: keep `CONN_MAX_AGE = 0` and configure pooling. - Templates: confirm whether `OPTIONS['loaders']` is set anywhere; if it is, wrap the list in the cached loader. ## What interviewers listen for The default of `0` and what it costs, the per-thread connection budget, the ASGI exception, and the fact that the cached loader is automatic unless someone has overridden `loaders`.
- A team adds a custom template loader and page render times rise noticeably. What likely happened?Listing `OPTIONS['loaders']` explicitly replaces Django's default, which wrapped the loaders in the cached loader. Without the wrapper every render re-reads and recompiles templates. Wrap the list in `('django.template.loaders.cached.Loader', [...])`, and include the app-directories loader in it since `APP_DIRS` cannot be combined with explicit loaders.
- Why not set CONN_MAX_AGE = None everywhere to save the most connection overhead?Each worker thread would then hold a connection for its whole life, idle or not, so the database needs a slot for every thread across all instances. Connections killed by the server or a network device also linger until a query fails unless `CONN_HEALTH_CHECKS` is on. Under ASGI the docs advise against persistent connections entirely in favour of pooling.
saying these in an interview costs you the question
- CONN_MAX_AGE defaults to persistent connections of several minutes
- The cached template loader must always be enabled by hand for production
- Listing custom loaders keeps Django's automatic caching wrapper
- Persistent connections are the recommended setup for ASGI deployments
- check --deploy warns when CONN_MAX_AGE is 0