mirror of
https://github.com/zulip/zulip.git
synced 2025-10-23 04:52:12 +00:00
We should not proceed and send client reload events until we know that all of the server processes have updated to the latest version, or they may reload into the old server version if they hit a Django worker which has not yet restarted. Because the logic controlling the number of workers is mildly complex, and lives in Puppet, use the `uwsgi` Python bindings to know when the process being reloaded is the last one, and use that to write out a file signifying the success of the chain reload. `restart-server` awaits the creation of this file before proceeding.
110 lines
4.1 KiB
Python
110 lines
4.1 KiB
Python
"""
|
|
WSGI config for zulip project.
|
|
|
|
This module contains the WSGI application used by Django's development server
|
|
and any production WSGI deployments. It should expose a module-level variable
|
|
named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
|
|
this application via the ``WSGI_APPLICATION`` setting.
|
|
|
|
Usually you will have the standard Django WSGI application here, but it also
|
|
might make sense to replace the whole Django WSGI application with a custom one
|
|
that later delegates to the Django one. For example, you could introduce WSGI
|
|
middleware here, or combine a Django application with an application of another
|
|
framework.
|
|
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
|
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
sys.path.append(BASE_DIR)
|
|
from scripts.lib.setup_path import setup_path
|
|
|
|
setup_path()
|
|
|
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "zproject.settings")
|
|
|
|
import contextlib
|
|
from collections.abc import Callable
|
|
from typing import Any
|
|
|
|
import orjson
|
|
from django.core.wsgi import get_wsgi_application
|
|
|
|
try:
|
|
# This application object is used by any WSGI server configured to use this
|
|
# file. This includes Django's development server, if the WSGI_APPLICATION
|
|
# setting points here.
|
|
|
|
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,
|
|
)
|
|
|
|
with contextlib.suppress(ModuleNotFoundError):
|
|
# The uwsgi module is only importable when running under
|
|
# uwsgi; development uses this file as well, but inside a
|
|
# pure-Python server. The surrounding contextmanager ensures
|
|
# that we don't bother with these steps if we're in
|
|
# development.
|
|
import uwsgi
|
|
|
|
if uwsgi.worker_id() == uwsgi.numproc:
|
|
# This is the last worker to load in the chain reload
|
|
with open("/var/lib/zulip/django-workers.ready", "wb") as f:
|
|
# The contents of this file are not read by restart-server
|
|
# in any way, but leave some useful information about the
|
|
# state of uwsgi.
|
|
f.write(
|
|
orjson.dumps(
|
|
uwsgi.workers(), option=orjson.OPT_INDENT_2, default=lambda e: e.decode()
|
|
),
|
|
)
|
|
|
|
except Exception:
|
|
# If /etc/zulip/settings.py contains invalid syntax, Django
|
|
# initialization will fail in django.setup(). In this case, our
|
|
# normal configuration to logs errors to /var/log/zulip/errors.log
|
|
# won't have been initialized. Since it's really valuable for the
|
|
# debugging process for a Zulip 500 error to always be "check
|
|
# /var/log/zulip/errors.log", we log to that file directly here.
|
|
import logging
|
|
|
|
logging.basicConfig(
|
|
filename="/var/log/zulip/errors.log",
|
|
level=logging.INFO,
|
|
format="%(asctime)s %(levelname)s %(name)s %(message)s",
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
logger.exception("get_wsgi_application() failed:")
|
|
raise
|