mirror of https://github.com/zulip/zulip.git
uwsgi: Force Django load before returning the uwsgi worker function.
Django lazy-loads much of its modules, including the application's. This defers the time to during the first request it serves. When doing rolling-restarts, this means that the worker is marked "ready" despite having multiple seconds more work to do. With small numbers of workers, this causes a significant capacity drop, since effectively more than one worker can be still reloading at a time. It also results in poor user experience for the requests which are unlucky enough to be served first. Use the technique detailed in the uwsgi documentation[^1] to fake a request during initial application load, which forces the application to be loaded. [^1]: https://uwsgi-docs.readthedocs.io/en/latest/articles/TheArtOfGracefulReloading.html#dealing-with-ultra-lazy-apps-like-django
This commit is contained in:
parent
8589becc48
commit
eef65d7e30
|
@ -25,6 +25,9 @@ setup_path()
|
||||||
|
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "zproject.settings")
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "zproject.settings")
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from django.core.wsgi import get_wsgi_application
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -33,6 +36,37 @@ try:
|
||||||
# setting points here.
|
# setting points here.
|
||||||
|
|
||||||
application = get_wsgi_application()
|
application = get_wsgi_application()
|
||||||
|
|
||||||
|
# We force loading of the main parts of the application now, by
|
||||||
|
# handing it a fake request, rather than have to pay that price
|
||||||
|
# during the first request served by this process. Hitting the
|
||||||
|
# /health endpoint will not only load Django and all of the views
|
||||||
|
# (by loading the URL dispatcher) but will also force open any
|
||||||
|
# lazy-loaded service connections.
|
||||||
|
#
|
||||||
|
# The return value (and thus response status) of this healthcheck
|
||||||
|
# request is ignored, so we do return the application handler even
|
||||||
|
# if connections are not fully available yet. This at least
|
||||||
|
# allows application logging to handle any such errors, instead of
|
||||||
|
# arcane errors from uwsgi not being able to load its handler
|
||||||
|
# function.
|
||||||
|
def ignored_start_response(
|
||||||
|
status: str, headers: list[tuple[str, str]], exc_info: Any = None, /
|
||||||
|
) -> Callable[[bytes], object]:
|
||||||
|
return lambda x: None
|
||||||
|
|
||||||
|
application(
|
||||||
|
{
|
||||||
|
"REQUEST_METHOD": "GET",
|
||||||
|
"SERVER_NAME": "127.0.0.1",
|
||||||
|
"SERVER_PORT": "443",
|
||||||
|
"PATH_INFO": "/health",
|
||||||
|
"REMOTE_ADDR": "127.0.0.1",
|
||||||
|
"wsgi.input": sys.stdin,
|
||||||
|
"wsgi.url_scheme": "https",
|
||||||
|
},
|
||||||
|
ignored_start_response,
|
||||||
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
# If /etc/zulip/settings.py contains invalid syntax, Django
|
# If /etc/zulip/settings.py contains invalid syntax, Django
|
||||||
# initialization will fail in django.setup(). In this case, our
|
# initialization will fail in django.setup(). In this case, our
|
||||||
|
|
Loading…
Reference in New Issue