mirror of https://github.com/zulip/zulip.git
restart-server: Wait until chain reload has completed.
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.
This commit is contained in:
parent
3efc5ae1fd
commit
674ca1a95d
|
@ -84,6 +84,7 @@ module = [
|
||||||
"tlds.*",
|
"tlds.*",
|
||||||
"twitter.*",
|
"twitter.*",
|
||||||
"two_factor.*",
|
"two_factor.*",
|
||||||
|
"uwsgi",
|
||||||
]
|
]
|
||||||
ignore_missing_imports = true
|
ignore_missing_imports = true
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
import contextlib
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import pwd
|
import pwd
|
||||||
|
@ -201,10 +202,19 @@ if has_application_server():
|
||||||
)
|
)
|
||||||
if uwsgi_status.returncode == 0:
|
if uwsgi_status.returncode == 0:
|
||||||
logging.info("Starting rolling restart of django server")
|
logging.info("Starting rolling restart of django server")
|
||||||
|
with contextlib.suppress(FileNotFoundError):
|
||||||
|
os.unlink("/var/lib/zulip/django-workers.ready")
|
||||||
with open("/home/zulip/deployments/uwsgi-control", "w") as control_socket:
|
with open("/home/zulip/deployments/uwsgi-control", "w") as control_socket:
|
||||||
# "c" is chain-reloading:
|
# "c" is chain-reloading:
|
||||||
# https://uwsgi-docs.readthedocs.io/en/latest/MasterFIFO.html#available-commands
|
# https://uwsgi-docs.readthedocs.io/en/latest/MasterFIFO.html#available-commands
|
||||||
control_socket.write("c")
|
control_socket.write("c")
|
||||||
|
n = 0
|
||||||
|
while not os.path.exists("/var/lib/zulip/django-workers.ready"):
|
||||||
|
time.sleep(1)
|
||||||
|
n += 1
|
||||||
|
if n % 5 == 0:
|
||||||
|
logging.info("...")
|
||||||
|
logging.info("Chain reloading complete")
|
||||||
else:
|
else:
|
||||||
logging.info("Starting django server")
|
logging.info("Starting django server")
|
||||||
subprocess.check_call(["supervisorctl", "start", "zulip-django"])
|
subprocess.check_call(["supervisorctl", "start", "zulip-django"])
|
||||||
|
|
|
@ -25,9 +25,11 @@ setup_path()
|
||||||
|
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "zproject.settings")
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "zproject.settings")
|
||||||
|
|
||||||
|
import contextlib
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
import orjson
|
||||||
from django.core.wsgi import get_wsgi_application
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -67,6 +69,27 @@ try:
|
||||||
},
|
},
|
||||||
ignored_start_response,
|
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:
|
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