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

fix(gevent): fix gevent compatibility with "module cloning" approach #4863

Merged
merged 120 commits into from
Feb 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
120 commits
Select commit Hold shift + click to select a range
53ae7e4
refactor(tracing): patch all integrations on import
P403n1x87 Oct 6, 2022
36a5f10
fix new contribs with failing patch on import tests
P403n1x87 Dec 1, 2022
fe85446
unload child modules too
P403n1x87 Dec 1, 2022
7b205b0
unload all modules in sitecustomize
P403n1x87 Dec 2, 2022
2bb028b
wip: gevent cleanup
P403n1x87 Jan 4, 2023
fb18a86
merge 1.x
emmettbutler Feb 1, 2023
cc5868c
missed a spot
emmettbutler Feb 1, 2023
e12a6f3
Merge branch '1.x' into cleanup-gevent
emmettbutler Feb 2, 2023
cfd7e7c
small clarifications from review discussion
emmettbutler Feb 3, 2023
94ac5bc
clearer comment
emmettbutler Feb 3, 2023
da7bf58
update test to work when gevent is not patched by ddtrace
emmettbutler Feb 6, 2023
18609e3
undo unnecessary change
emmettbutler Feb 6, 2023
7b8bcbc
put another gevent test in a subprocess
emmettbutler Feb 6, 2023
4212c00
check for main thread properly
emmettbutler Feb 6, 2023
21b75ac
Merge branch '1.x' into cleanup-gevent
emmettbutler Feb 7, 2023
d7a7037
add ddtrace-patch to working configs in test
emmettbutler Feb 7, 2023
c093b46
fix broken test
emmettbutler Feb 7, 2023
b72a2a5
flake8
emmettbutler Feb 7, 2023
8a5e635
flake8
emmettbutler Feb 7, 2023
05f55ee
Update ddtrace/bootstrap/sitecustomize.py
emmettbutler Feb 7, 2023
030ab6e
Update ddtrace/bootstrap/sitecustomize.py
emmettbutler Feb 7, 2023
46d6e35
Update ddtrace/bootstrap/sitecustomize.py
emmettbutler Feb 7, 2023
d9a80f0
merge from remote
emmettbutler Feb 7, 2023
390321b
undo changes in compat.py since they are unrelated to gevent compatib…
emmettbutler Feb 7, 2023
b8b8016
documentation
emmettbutler Feb 7, 2023
69787d1
clearer py2 comment
emmettbutler Feb 7, 2023
3dd4352
document envvar in release note
emmettbutler Feb 7, 2023
5f886f3
detect gevent install
emmettbutler Feb 7, 2023
b2f82e6
document envvar in config list
emmettbutler Feb 7, 2023
c7793c7
Update tests/internal/test_forksafe.py
emmettbutler Feb 7, 2023
30e005a
update commented nogevent uses
emmettbutler Feb 7, 2023
29d73a8
use imp.find_module properly
emmettbutler Feb 7, 2023
6401686
flake8
emmettbutler Feb 7, 2023
df0492c
remove unused code from gunicorn test app
emmettbutler Feb 7, 2023
6894abe
remove stuff related to gevent patching from gunicorn tests
emmettbutler Feb 7, 2023
772ca55
try to fix docs formatting
emmettbutler Feb 7, 2023
1dcb95f
remove failing test
emmettbutler Feb 7, 2023
151fe7e
try a different style of import
emmettbutler Feb 7, 2023
44137a8
docs formatting error
emmettbutler Feb 7, 2023
893da29
fix spellchecks
emmettbutler Feb 7, 2023
343c0ec
remove failing test
emmettbutler Feb 7, 2023
51e069e
try a double import
emmettbutler Feb 7, 2023
40f78ed
noqa
emmettbutler Feb 7, 2023
75f4a68
fix spellcheck in release note
emmettbutler Feb 7, 2023
82aaaac
re-add testing flags
emmettbutler Feb 7, 2023
18b5325
Update releasenotes/notes/gevent-compatibility-0fe0623c602d7617.yaml
emmettbutler Feb 7, 2023
9d48377
Merge branch '1.x' into cleanup-gevent
emmettbutler Feb 7, 2023
f694cdc
remove pointless file
emmettbutler Feb 7, 2023
7e2ae59
remove pointless file
emmettbutler Feb 7, 2023
02e0f01
thinner slice for version testing
emmettbutler Feb 7, 2023
41e7058
remove unnecessary import
emmettbutler Feb 8, 2023
d19ced7
fix import
emmettbutler Feb 8, 2023
c2fe25c
twiddle some numbers in profile tests
emmettbutler Feb 8, 2023
c9a2fe7
revert unintentional whitespace change
emmettbutler Feb 8, 2023
1f759f5
merge 1.x
emmettbutler Feb 9, 2023
0af0472
Update ddtrace/internal/debug.py
emmettbutler Feb 10, 2023
92c4fdc
make modules to cleanup easier to change
emmettbutler Feb 10, 2023
8dfbec3
use sets to make the cleanup logic a little more declarative
emmettbutler Feb 10, 2023
b89a8e2
todo comment in riotfile about gunicorn
emmettbutler Feb 10, 2023
4a267ce
revert logging tests
emmettbutler Feb 10, 2023
3421e82
clean up duplicate imports
emmettbutler Feb 10, 2023
4b64719
Update ddtrace/bootstrap/sitecustomize.py
emmettbutler Feb 10, 2023
a1a9d1c
Update ddtrace/bootstrap/sitecustomize.py
emmettbutler Feb 10, 2023
ccde165
update debug message to match format
emmettbutler Feb 10, 2023
24c4054
update gevent-related profiling stack tests
emmettbutler Feb 10, 2023
389db52
undo nogevent removal
emmettbutler Feb 10, 2023
f9b139e
update function name to clarify what it does
emmettbutler Feb 10, 2023
6a0010a
Update ddtrace/internal/debug.py
emmettbutler Feb 10, 2023
bd46982
Update ddtrace/bootstrap/sitecustomize.py
emmettbutler Feb 10, 2023
4081e65
syntax error
emmettbutler Feb 10, 2023
acf5c01
remove test changes that were moved to #5105
emmettbutler Feb 10, 2023
a45648c
use faster and more idiomatic set syntax
emmettbutler Feb 10, 2023
2cbdbf9
whitespace
emmettbutler Feb 10, 2023
c21a648
slice out changes moved to 5109
emmettbutler Feb 10, 2023
6d84601
slice out changes moved to 5109
emmettbutler Feb 10, 2023
2f77282
slice out changes moved to #5104
emmettbutler Feb 10, 2023
9bf62d1
default to no module cloning
emmettbutler Feb 10, 2023
fc26686
revert documentation changes pulled into #5109
emmettbutler Feb 10, 2023
1642557
add note about default flag value to releasenote
emmettbutler Feb 10, 2023
401eade
revert unintentional whitespace change
emmettbutler Feb 10, 2023
f4c4db3
set flag in tests
emmettbutler Feb 10, 2023
6557b90
re-add important file
emmettbutler Feb 10, 2023
268ed42
make sure sys and importlib also get unloaded
emmettbutler Feb 10, 2023
d40d3d9
whitespace
emmettbutler Feb 10, 2023
5d56bf0
Merge branch '1.x' into cleanup-gevent
emmettbutler Feb 10, 2023
00c62e3
undo unnecessary change
emmettbutler Feb 10, 2023
5786253
undo change moved to #5105
emmettbutler Feb 10, 2023
90c95ca
undo unnecessary changes
emmettbutler Feb 10, 2023
7131f21
undo unnecessary changes
emmettbutler Feb 10, 2023
a242f22
move changes to #5105
emmettbutler Feb 10, 2023
e9d39da
unload before gevent patch if necessary
emmettbutler Feb 11, 2023
3c64d4b
set module lists early so they are available when needed
emmettbutler Feb 11, 2023
7bfa2d1
don't save references when forcing unload, even under py2
emmettbutler Feb 11, 2023
323113f
be even more aggressive in module unloading to prepare for gevent patch
emmettbutler Feb 11, 2023
a756a01
move definition for clarity
emmettbutler Feb 11, 2023
c843b20
avoid loading compat module to keep sys.modules as clean as possible …
emmettbutler Feb 11, 2023
3482403
better naming
emmettbutler Feb 11, 2023
6830470
remove outdated comment
emmettbutler Feb 11, 2023
89d9f42
clearer naming
emmettbutler Feb 11, 2023
2c2cdd8
undo unnecessary changes
emmettbutler Feb 12, 2023
964bf72
add back important regression test
emmettbutler Feb 13, 2023
e6a1e3a
this was important
emmettbutler Feb 13, 2023
e9e0265
simplify decision logic in cleanup_loaded_modules
emmettbutler Feb 13, 2023
74ef12f
unloading typing module might be ok
emmettbutler Feb 13, 2023
71ef7d7
add comment
emmettbutler Feb 13, 2023
2ddc8d8
update default in documentation
emmettbutler Feb 13, 2023
b9c9647
fix broken version check
emmettbutler Feb 13, 2023
e10d8a4
Merge branch '1.x' into cleanup-gevent
emmettbutler Feb 13, 2023
07638b2
remove duplicate calls
emmettbutler Feb 13, 2023
73b0cd9
Update ddtrace/bootstrap/sitecustomize.py
emmettbutler Feb 13, 2023
76a393a
Update ddtrace/bootstrap/sitecustomize.py
emmettbutler Feb 13, 2023
89e744f
delete time module cleanup
emmettbutler Feb 13, 2023
0736ee6
deeper contextual comment
emmettbutler Feb 13, 2023
c7f39bb
Update docs/configuration.rst
emmettbutler Feb 13, 2023
0d23724
Update ddtrace/bootstrap/sitecustomize.py
emmettbutler Feb 13, 2023
142c737
simplify a bit from review
emmettbutler Feb 13, 2023
035908c
whitespace
emmettbutler Feb 13, 2023
0aaa736
cleanups from review
emmettbutler Feb 13, 2023
1384c16
whitespace
emmettbutler Feb 13, 2023
c25156c
Merge branch '1.x' into cleanup-gevent
emmettbutler Feb 13, 2023
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
117 changes: 103 additions & 14 deletions ddtrace/bootstrap/sitecustomize.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,108 @@
Bootstrapping code that is run when using the `ddtrace-run` Python entrypoint
Add all monkey-patching that needs to run by default here
"""
import logging
import os
import sys
from typing import Any
from typing import Dict


# Perform gevent patching as early as possible in the application before
# importing more of the library internals.
if os.environ.get("DD_GEVENT_PATCH_ALL", "false").lower() in ("true", "1"):
emmettbutler marked this conversation as resolved.
Show resolved Hide resolved
import gevent.monkey
MODULES_LOADED_AT_STARTUP = frozenset(sys.modules.keys())
emmettbutler marked this conversation as resolved.
Show resolved Hide resolved
MODULES_THAT_TRIGGER_CLEANUP_WHEN_INSTALLED = ("gevent",)

gevent.monkey.patch_all()
emmettbutler marked this conversation as resolved.
Show resolved Hide resolved

import os # noqa


MODULES_TO_NOT_CLEANUP = {"atexit", "asyncio", "attr", "concurrent", "ddtrace", "logging"}
if sys.version_info <= (2, 7):
MODULES_TO_NOT_CLEANUP |= {"encodings", "codecs"}
import imp

_unloaded_modules = []

def is_installed(module_name):
try:
imp.find_module(module_name)
except ImportError:
return False
return True


else:
import importlib

def is_installed(module_name):
return importlib.util.find_spec(module_name)


def should_cleanup_loaded_modules():
emmettbutler marked this conversation as resolved.
Show resolved Hide resolved
dd_unload_sitecustomize_modules = os.getenv("DD_UNLOAD_MODULES_FROM_SITECUSTOMIZE", default="0").lower()
if dd_unload_sitecustomize_modules not in ("1", "auto"):
return False
elif dd_unload_sitecustomize_modules == "auto" and not any(
is_installed(module_name) for module_name in MODULES_THAT_TRIGGER_CLEANUP_WHEN_INSTALLED
):
return False
return True


def cleanup_loaded_modules(aggressive=False):
"""
"Aggressive" here means "cleanup absolutely every module that has been loaded since startup".
Non-aggressive cleanup entails leaving untouched certain modules
This distinction is necessary because this function is used both to prepare for gevent monkeypatching
(requiring aggressive cleanup) and to implement "module cloning" (requiring non-aggressive cleanup)
"""
# Figuring out modules_loaded_since_startup is necessary because sys.modules has more in it than just what's in
# import statements in this file, and unloading some of them can break the interpreter.
emmettbutler marked this conversation as resolved.
Show resolved Hide resolved
modules_loaded_since_startup = set(_ for _ in sys.modules if _ not in MODULES_LOADED_AT_STARTUP)
# Unload all the modules that we have imported, except for ddtrace and a few
# others that don't like being cloned.
# Doing so will allow ddtrace to continue using its local references to modules unpatched by
# gevent, while avoiding conflicts with user-application code potentially running
# `gevent.monkey.patch_all()` and thus gevent-patched versions of the same modules.
for module_name in modules_loaded_since_startup:
if aggressive:
del sys.modules[module_name]
continue

for module_to_not_cleanup in MODULES_TO_NOT_CLEANUP:
if module_name == module_to_not_cleanup:
break
elif module_name.startswith("%s." % module_to_not_cleanup):
break
else:
del sys.modules[module_name]


will_run_module_cloning = should_cleanup_loaded_modules()
if not will_run_module_cloning:
# Perform gevent patching as early as possible in the application before
# importing more of the library internals.
if os.environ.get("DD_GEVENT_PATCH_ALL", "false").lower() in ("true", "1"):
# successfully running `gevent.monkey.patch_all()` this late into
# sitecustomize requires aggressive module unloading beforehand.
emmettbutler marked this conversation as resolved.
Show resolved Hide resolved
# gevent's documentation strongly warns against calling monkey.patch_all() anywhere other
# than the first line of the program. since that's what we're doing here,
# we cleanup aggressively beforehand to replicate the conditions at program start
# as closely as possible.
cleanup_loaded_modules(aggressive=True)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose we can be aggressive here because we still haven't loaded much?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's probably true.

import gevent.monkey

gevent.monkey.patch_all()

import logging # noqa
import os # noqa
from typing import Any # noqa
from typing import Dict # noqa

from ddtrace import config # noqa
from ddtrace.debugging._config import config as debugger_config
from ddtrace.debugging._config import config as debugger_config # noqa
from ddtrace.internal.logger import get_logger # noqa
from ddtrace.internal.runtime.runtime_metrics import RuntimeWorker
from ddtrace.internal.runtime.runtime_metrics import RuntimeWorker # noqa
from ddtrace.internal.utils.formats import asbool # noqa
from ddtrace.internal.utils.formats import parse_tags_str
from ddtrace.internal.utils.formats import parse_tags_str # noqa
from ddtrace.tracer import DD_LOG_FORMAT # noqa
from ddtrace.tracer import debug_mode
from ddtrace.vendor.debtcollector import deprecate
from ddtrace.tracer import debug_mode # noqa
from ddtrace.vendor.debtcollector import deprecate # noqa


if config.logs_injection:
Expand Down Expand Up @@ -107,12 +185,23 @@ def update_patched_modules():
if not opts:
tracer.configure(**opts)

# We need to clean up after we have imported everything we need from
# ddtrace, but before we register the patch-on-import hooks for the
# integrations. This is because registering a hook for a module
# that is already imported causes the module to be patched immediately.
# So if we unload the module after registering hooks, we effectively
# remove the patching, thus breaking the tracer integration.
if will_run_module_cloning:
emmettbutler marked this conversation as resolved.
Show resolved Hide resolved
cleanup_loaded_modules()
if trace_enabled:
update_patched_modules()
from ddtrace import patch_all

patch_all(**EXTRA_PATCHED_MODULES)

# Only the import of the original sitecustomize.py is allowed after this
# point.

if "DD_TRACE_GLOBAL_TAGS" in os.environ:
env_tags = os.getenv("DD_TRACE_GLOBAL_TAGS")
tracer.set_tags(parse_tags_str(env_tags))
Expand Down
1 change: 1 addition & 0 deletions ddtrace/internal/telemetry/writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ def on_shutdown(self):
def _stop_service(self, *args, **kwargs):
# type: (...) -> None
super(TelemetryWriter, self)._stop_service(*args, **kwargs)
# TODO: Call this with an atexit hook
emmettbutler marked this conversation as resolved.
Show resolved Hide resolved
self.join()

def add_event(self, payload, payload_type):
Expand Down
31 changes: 21 additions & 10 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -223,10 +223,10 @@ The following environment variables for the tracer are supported:
DD_SPAN_SAMPLING_RULES:
type: string
description: |
A JSON array of objects. Each object must have a "name" and/or "service" field, while the "max_per_second" and "sample_rate" fields are optional.
A JSON array of objects. Each object must have a "name" and/or "service" field, while the "max_per_second" and "sample_rate" fields are optional.
The "sample_rate" value must be between 0.0 and 1.0 (inclusive), and will default to 1.0 (100% sampled).
The "max_per_second" value must be >= 0 and will default to no limit.
The "service" and "name" fields can be glob patterns:
The "max_per_second" value must be >= 0 and will default to no limit.
The "service" and "name" fields can be glob patterns:
"*" matches any substring, including the empty string,
"?" matches exactly one of any character, and any other character matches exactly one of itself.

Expand All @@ -238,11 +238,11 @@ The following environment variables for the tracer are supported:
DD_SPAN_SAMPLING_RULES_FILE:
type: string
description: |
A path to a JSON file containing span sampling rules organized as JSON array of objects.
For the rules each object must have a "name" and/or "service" field, and the "sample_rate" field is optional.
A path to a JSON file containing span sampling rules organized as JSON array of objects.
For the rules each object must have a "name" and/or "service" field, and the "sample_rate" field is optional.
The "sample_rate" value must be between 0.0 and 1.0 (inclusive), and will default to 1.0 (100% sampled).
The "max_per_second" value must be >= 0 and will default to no limit.
The "service" and "name" fields are glob patterns, where "glob" means:
The "max_per_second" value must be >= 0 and will default to no limit.
The "service" and "name" fields are glob patterns, where "glob" means:
"*" matches any substring, including the empty string,
"?" matches exactly one of any character, and any other character matches exactly one of itself.

Expand Down Expand Up @@ -287,7 +287,7 @@ The following environment variables for the tracer are supported:
The supported values are ``datadog``, ``b3multi``, and ``b3 single header``, and ``none``.

When checking inbound request headers we will take the first valid trace context in the order provided.
When ``none`` is the only propagator listed, propagation is disabled.
When ``none`` is the only propagator listed, propagation is disabled.

All provided styles are injected into the headers of outbound requests.

Expand All @@ -308,7 +308,7 @@ The following environment variables for the tracer are supported:
The supported values are ``datadog``, ``b3multi``, and ``b3 single header``, and ``none``.

When checking inbound request headers we will take the first valid trace context in the order provided.
When ``none`` is the only propagator listed, extraction is disabled.
When ``none`` is the only propagator listed, extraction is disabled.

Example: ``DD_TRACE_PROPAGATION_STYLE="datadog,b3"`` to check for both ``x-datadog-*`` and ``x-b3-*``
headers when parsing incoming request headers for a trace context. In addition, to inject both ``x-datadog-*`` and ``x-b3-*``
Expand All @@ -328,7 +328,7 @@ The following environment variables for the tracer are supported:
The supported values are ``datadog``, ``b3multi``, and ``b3 single header``, and ``none``.

All provided styles are injected into the headers of outbound requests.
When ``none`` is the only propagator listed, injection is disabled.
When ``none`` is the only propagator listed, injection is disabled.

Example: ``DD_TRACE_PROPAGATION_STYLE_INJECT="datadog,b3multi"`` to inject both ``x-datadog-*`` and ``x-b3-*``
headers into outbound requests.
Expand Down Expand Up @@ -473,6 +473,17 @@ The following environment variables for the tracer are supported:
default: "DES,Blowfish,RC2,RC4,IDEA"
description: Weak cipher algorithms that should be reported, comma separated.

DD_UNLOAD_MODULES_FROM_SITECUSTOMIZE:
Yun-Kim marked this conversation as resolved.
Show resolved Hide resolved
type: String
default: "0"
description: |
Controls whether module cloning logic is executed by ``ddtrace-run``. Module cloning involves saving copies of dependency modules for internal use by ``ddtrace``
that will be unaffected by future imports of and changes to those modules by application code. Valid values for this variable are ``1``, ``0``, and ``auto``. ``1`` tells
``ddtrace`` to run its module cloning logic unconditionally, ``0`` tells it not to run that logic, and ``auto`` tells it to run module cloning logic only if ``gevent``
is accessible from the application's runtime.
version_added:
v1.9.0:

.. _Unified Service Tagging: https://docs.datadoghq.com/getting_started/tagging/unified_service_tagging/


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
fixes:
- |
gevent: This fix resolves incompatibility under 3.8>=Python<=3.10 between ``ddtrace-run`` and applications that depend on ``gevent``, for example ``gunicorn`` servers. It accomplishes this by
keeping copies that have not been monkey patched by ``gevent`` of most modules used by ``ddtrace``. This "module cloning" logic can be controlled by the environment
variable ``DD_UNLOAD_MODULES_FROM_SITECUSTOMIZE``. Valid values for this variable are "1", "0", and "auto". "1" tells ``ddtrace`` to run its module cloning logic
unconditionally, "0" tells it never to run that logic, and "auto" tells it to run module cloning logic *only if* ``gevent`` is accessible from the application's runtime.
The default value is "0".
emmettbutler marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion riotfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -2604,7 +2604,7 @@ def select_pys(min_version=MIN_PYTHON_VERSION, max_version=MAX_PYTHON_VERSION):
pkgs={"requests": latest, "gevent": latest},
venvs=[
Venv(
pys=select_pys(min_version="3.5"),
pys=select_pys(min_version="3.8"),
emmettbutler marked this conversation as resolved.
Show resolved Hide resolved
pkgs={"gunicorn": ["==19.10.0", "==20.0.4", latest]},
),
],
Expand Down
14 changes: 9 additions & 5 deletions tests/contrib/gunicorn/test_gunicorn.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ def _gunicorn_settings_factory(
patch_gevent=None, # type: Optional[bool]
import_sitecustomize_in_app=None, # type: Optional[bool]
start_service_in_hook_named="post_fork", # type: str
enable_module_cloning=False, # type: bool
):
# type: (...) -> GunicornServerSettings
"""Factory for creating gunicorn settings with simple defaults if settings are not defined."""
Expand All @@ -85,6 +86,7 @@ def _gunicorn_settings_factory(
env["DD_GEVENT_PATCH_ALL"] = str(patch_gevent)
if import_sitecustomize_in_app is not None:
env["_DD_TEST_IMPORT_SITECUSTOMIZE"] = str(import_sitecustomize_in_app)
env["DD_UNLOAD_MODULES_FROM_SITECUSTOMIZE"] = "1" if enable_module_cloning else "0"
env["DD_REMOTECONFIG_POLL_SECONDS"] = str(SERVICE_INTERVAL)
env["DD_PROFILING_UPLOAD_INTERVAL"] = str(SERVICE_INTERVAL)
return GunicornServerSettings(
Expand Down Expand Up @@ -167,10 +169,10 @@ def gunicorn_server(gunicorn_server_settings, tmp_path):
server_process.wait()


SETTINGS_GEVENT_DDTRACERUN_PATCH = _gunicorn_settings_factory(
worker_class="gevent",
patch_gevent=True,
SETTINGS_GEVENT_DDTRACERUN_MODULE_CLONE = _gunicorn_settings_factory(
worker_class="gevent", patch_gevent=False, enable_module_cloning=True
)
SETTINGS_GEVENT_DDTRACERUN_PATCH = _gunicorn_settings_factory(worker_class="gevent", patch_gevent=True)
SETTINGS_GEVENT_APPIMPORT_PATCH_POSTWORKERSERVICE = _gunicorn_settings_factory(
worker_class="gevent",
use_ddtracerun=False,
Expand All @@ -187,19 +189,21 @@ def gunicorn_server(gunicorn_server_settings, tmp_path):
)


@pytest.mark.skipif(sys.version_info > (3, 10), reason="Gunicorn is only supported up to 3.10")
@pytest.mark.parametrize(
"gunicorn_server_settings",
[
SETTINGS_GEVENT_APPIMPORT_PATCH_POSTWORKERSERVICE,
SETTINGS_GEVENT_POSTWORKERIMPORT_PATCH_POSTWORKERSERVICE,
SETTINGS_GEVENT_DDTRACERUN_MODULE_CLONE,
],
)
def test_no_known_errors_occur(gunicorn_server_settings, tmp_path):
with gunicorn_server(gunicorn_server_settings, tmp_path) as context:
server_process, client = context
r = client.get("/")
assert_no_profiler_error(server_process)
assert_remoteconfig_started_successfully(r)
assert_remoteconfig_started_successfully(r, gunicorn_server_settings.env["DD_GEVENT_PATCH_ALL"] == "True")


@pytest.mark.parametrize(
Expand All @@ -213,6 +217,6 @@ def test_profiler_error_occurs_under_gevent_worker(gunicorn_server_settings, tmp
server_process, client = context
r = client.get("/")
# this particular error does not manifest in 3.8 and older
if sys.version_info[1] > 8:
if sys.version_info >= (3, 9):
assert MOST_DIRECT_KNOWN_GUNICORN_RELATED_PROFILER_ERROR_SIGNAL in server_process.stderr.read()
assert_remoteconfig_started_successfully(r)