#36964: Clarify how persistent connections interact with runserver
-------------------------------------+-------------------------------------
Reporter: Adam Sołtysik | Owner: Youssef
Type: | Tarek Ali
Cleanup/optimization | Status: assigned
Component: Documentation | Version: 5.2
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Youssef Tarek Ali):
* has_patch: 0 => 1
Comment:
Replying to [comment:10 Tim Graham]:
> The quoted statement in the documentation was written by one of Django's
most esteemed contributors (2ee21d9f0d9eaed0494f3b9cd4b5bc9beffffae5).
While something may have changed in the intervening years, we need a more
rigorous explanation.
>
> I believe that multiple threads may be used by the built-in runserver,
even if requests are not concurrent. I verified this making this
modification:
> {{{
> diff --git a/django/db/backends/base/base.py
b/django/db/backends/base/base.py
> index 23015a57a3..94e7197dde 100644
> --- a/django/db/backends/base/base.py
> +++ b/django/db/backends/base/base.py
> @@ -51,6 +51,9 @@ class BaseDatabaseWrapper:
> queries_limit = 9000
>
> def __init__(self, settings_dict, alias=DEFAULT_DB_ALIAS):
> + print("DB Wrapper")
> + import threading
> + print(threading.get_ident())
> # Connection related attributes.
> # The underlying database connection.
> self.connection = None
> }}}
> And making several requests:
> {{{
> Starting WSGI development server at
http://127.0.0.1:8000/
> Quit the server with CONTROL-C.
>
> DB Wrapper
> 139299259958976
> [14/Mar/2026 00:41:02] "GET /admin/ HTTP/1.1" 200 11255
> [14/Mar/2026 00:41:04] "GET /admin/polls/choice/ HTTP/1.1" 200 11343
> [14/Mar/2026 00:41:04] "GET /admin/jsi18n/ HTTP/1.1" 200 3342
> [14/Mar/2026 00:41:08] "GET /admin/polls/question/ HTTP/1.1" 200 13477
> DB Wrapper
> 139299250517696
> [14/Mar/2026 00:41:09] "GET /admin/jsi18n/ HTTP/1.1" 200 3342
> [14/Mar/2026 00:41:12] "GET /admin/polls/choice/ HTTP/1.1" 200 1134
> }}}
> I don't believe the AI-generated patch correctly explained the nuances
here.
Thanks for the logs and feedback, Natalia. I see now that my previous
rationale was off.
I wrote this update myself to make sure the technical details are correct.
The new PR explains that database connections are thread-local and that
there's no guarantee of thread reuse for sequential requests, even with
Keep-Alive. I also added a note about the auto-reloader.
The documentation builds correctly locally. Let me know if I've still
missed anything.
New PR:
https://github.com/django/django/pull/20905
--
Ticket URL: <
https://code.djangoproject.com/ticket/36964#comment:11>
Django <
https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.