"""Gunicorn + SQS worker in one task. Stay a parent so SIGTERM reaches both.""" from __future__ import annotations import os import signal import subprocess import sys import time def main() -> None: env = os.environ.copy() worker = subprocess.Popen( [sys.executable, "-m", "server.worker"], env=env, ) gunicorn = subprocess.Popen( [ "gunicorn", "--bind", "0.0.0.0:8080", "--workers", os.environ.get("GUNICORN_WORKERS", "2"), "--threads", "2", "--timeout", "120", "--graceful-timeout", "30", "--access-logfile", "-", "--error-logfile", "-", "server.wsgi:app", ], env=env, ) def shutdown(signum: int, _frame) -> None: for proc in (gunicorn, worker): if proc.poll() is None: proc.send_signal(signum) signal.signal(signal.SIGTERM, shutdown) signal.signal(signal.SIGINT, shutdown) while True: g_code = gunicorn.poll() w_code = worker.poll() if g_code is not None: if worker.poll() is None: worker.terminate() try: worker.wait(timeout=30) except subprocess.TimeoutExpired: worker.kill() sys.exit(g_code) if w_code is not None: if gunicorn.poll() is None: gunicorn.terminate() try: gunicorn.wait(timeout=30) except subprocess.TimeoutExpired: gunicorn.kill() sys.exit(w_code or 1) time.sleep(1) if __name__ == "__main__": main()