From 1e3df09fc6462bb03be1c0b9e37ccc5b414d0e04 Mon Sep 17 00:00:00 2001 From: QuantumGhost Date: Mon, 22 Sep 2025 18:23:01 +0800 Subject: [PATCH] chore(api): adjust monkey patching in gunicorn.conf.py (#26056) --- api/celery_entrypoint.py | 13 ++----------- api/gunicorn.conf.py | 28 +++++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/api/celery_entrypoint.py b/api/celery_entrypoint.py index 4d1f17430d..28fa0972e8 100644 --- a/api/celery_entrypoint.py +++ b/api/celery_entrypoint.py @@ -1,20 +1,11 @@ -import logging - import psycogreen.gevent as pscycogreen_gevent # type: ignore from grpc.experimental import gevent as grpc_gevent # type: ignore -_logger = logging.getLogger(__name__) - - -def _log(message: str): - _logger.debug(message) - - # grpc gevent grpc_gevent.init_gevent() -_log("gRPC patched with gevent.") +print("gRPC patched with gevent.", flush=True) # noqa: T201 pscycogreen_gevent.patch_psycopg() -_log("psycopg2 patched with gevent.") +print("psycopg2 patched with gevent.", flush=True) # noqa: T201 from app import app, celery diff --git a/api/gunicorn.conf.py b/api/gunicorn.conf.py index fc91a43670..943ee100ca 100644 --- a/api/gunicorn.conf.py +++ b/api/gunicorn.conf.py @@ -1,10 +1,32 @@ import psycogreen.gevent as pscycogreen_gevent # type: ignore +from gevent import events as gevent_events from grpc.experimental import gevent as grpc_gevent # type: ignore +# NOTE(QuantumGhost): here we cannot use post_fork to patch gRPC, as +# grpc_gevent.init_gevent must be called after patching stdlib. +# Gunicorn calls `post_init` before applying monkey patch. +# Use `post_init` to setup gRPC gevent support would cause deadlock and +# some other weird issues. +# +# ref: +# - https://github.com/grpc/grpc/blob/62533ea13879d6ee95c6fda11ec0826ca822c9dd/src/python/grpcio/grpc/experimental/gevent.py +# - https://github.com/gevent/gevent/issues/2060#issuecomment-3016768668 +# - https://github.com/benoitc/gunicorn/blob/master/gunicorn/arbiter.py#L607-L613 -def post_fork(server, worker): + +def post_patch(event): + # this function is only called for gevent worker. + # from gevent docs (https://www.gevent.org/api/gevent.monkey.html): + # You can also subscribe to the events to provide additional patching beyond what gevent distributes, either for + # additional standard library modules, or for third-party packages. The suggested time to do this patching is in + # the subscriber for gevent.events.GeventDidPatchBuiltinModulesEvent. + if not isinstance(event, gevent_events.GeventDidPatchBuiltinModulesEvent): + return # grpc gevent grpc_gevent.init_gevent() - server.log.info("gRPC patched with gevent.") + print("gRPC patched with gevent.", flush=True) # noqa: T201 pscycogreen_gevent.patch_psycopg() - server.log.info("psycopg2 patched with gevent.") + print("psycopg2 patched with gevent.", flush=True) # noqa: T201 + + +gevent_events.subscribers.append(post_patch)