Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(gevent): run after-in-child hooks after reinit #4070

Merged
merged 15 commits into from
Aug 19, 2022
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions ddtrace/internal/forksafe.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import typing
import weakref

from ddtrace.internal.module import ModuleWatchdog
from ddtrace.vendor import wrapt


Expand All @@ -22,6 +23,25 @@
_soft = False


def patch_gevent_hub_reinit(module):
# The gevent hub is re-initialized *after* the after-in-child fork hooks are
# called, so we patch the gevent.hub.reinit function to ensure that the
# fork hooks run again after this further re-initialisation, if it is ever
# called.
from ddtrace.internal.wrapping import wrap

def wrapped_reinit(f, args, kwargs):
try:
return f(*args, **kwargs)
finally:
ddtrace_after_in_child()

wrap(module.reinit, wrapped_reinit)


ModuleWatchdog.register_module_hook("gevent.hub", patch_gevent_hub_reinit)


def ddtrace_after_in_child():
# type: () -> None
global _registry
Expand Down
1 change: 1 addition & 0 deletions riotfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ def select_pys(min_version=MIN_PYTHON_VERSION, max_version=MAX_PYTHON_VERSION):
"structlog": latest,
# httpretty v1.0 drops python 2.7 support
"httpretty": "==0.9.7",
"gevent": latest,
},
)
],
Expand Down
57 changes: 57 additions & 0 deletions tests/tracer/test_forksafe.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import sys

import pytest
import six
Expand Down Expand Up @@ -285,3 +286,59 @@ def fn():
pid, status = os.waitpid(child, 0)
exit_code = os.WEXITSTATUS(status)
assert exit_code == 42


@pytest.mark.subprocess(
out="" if (3,) < sys.version_info < (3, 7) else ("CTCTCT" if sys.platform == "darwin" else "CCCTTT"),
P403n1x87 marked this conversation as resolved.
Show resolved Hide resolved
err=None,
)
def test_gevent_reinit_patch():
import os
import sys

from ddtrace.internal import forksafe
from ddtrace.internal.periodic import PeriodicService

class TestService(PeriodicService):
def __init__(self):
super(TestService, self).__init__(interval=1.0)

def periodic(self):
sys.stdout.write("T")

service = TestService()
service.start()

def restart_service():
global service

service.stop()
service = TestService()
service.start()

forksafe.register(restart_service)

import gevent # noqa

def run_child():
global service

# We mimic what gunicorn does in child processes
gevent.monkey.patch_all()
gevent.hub.reinit()

sys.stdout.write("C")

gevent.sleep(1.5)

service.stop()

def fork_workers(num):
for _ in range(num):
if os.fork() == 0:
run_child()
sys.exit(0)

fork_workers(3)

service.stop()