-
Notifications
You must be signed in to change notification settings - Fork 455
docs(uwsgi): update docs on --lazy-apps
for uwsgi and tests
#13550
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
Conversation
|
Bootstrap import analysisComparison of import times between this PR and base. SummaryThe average import time from this PR is: 236 ± 3 ms. The average import time from base is: 237 ± 3 ms. The import time difference between this PR and base is: -1.0 ± 0.1 ms. Import time breakdownThe following import paths have shrunk:
|
BenchmarksBenchmark execution time: 2025-06-03 16:14:30 Comparing candidate commit d03bfb7 in PR branch Found 0 performance improvements and 2 performance regressions! Performance is the same for 503 metrics, 3 unstable metrics. scenario:iastaspectsospath-ospathnormcase_aspect
scenario:telemetryaddmetric-1-distribution-metric-1-times
|
…trace-py into taegyunkim/uwsgi-atexit-hook
releasenotes/notes/profiling-uwsgi-skip-atexit-2d931caef96837b1.yaml
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work fixing the problem upstream!
…trace-py into taegyunkim/uwsgi-atexit-hook
tl;dr
uwsgi>=2.0.30
which has fix: install atexit handlers after apps are initialized unbit/uwsgi#2726uwsgi<2.0.30
with--lazy-apps
, also set--skip-atexit
, if you see crashes when workers exit.The following explanation happens on
uwsgi<2.0.30
When
uwsgi
is configured with--lazy-apps
, master doesn't load the application, and simplyfork()
to create workers. Then, the workers load the application. This is different from when it's not set. Without--lazy-apps
, master process first load the application thenfork()
.dd-trace-py profiler uses Python stdlib
atexit
module to export the profile when the process exits. The code run to export references global staticstd::string
variables. However, when--lazy-apps
option is set, they are destructed before when the profiler needs them to export the profile, and this leads to an undefined behavior.When
--lazy-apps
is not set the following sequence of events happensimport ddtrace.profiling.auto
and this leads all native extensions imported to the Python app. During the process, the constructors for global static variables are called and their destructors would be registered to be called byatexit()
when the program exits.Profiler.stop()
function touwsgi
python module'satexit
attribute.uwsgi
callsatexit(uwsgi_plugins_atexit)
to register each plugins atexit callbacks, for Pythonuwsgi_python_atexit()
function. And the function will callPy_Finalize()
fork()
s workersuwsgi_python_atexit()
is called, invokingProfiler.stop()
andPy_Finalize()
is called, then destructors for global static variables are called.When
--lazy-apps
is setuwsgi
callsatexit(uwsgi_plugins_atexit)
to register each plugins atexit callbacks, for Pythonuwsgi_python_atexit()
function. And the function will callPy_Finalize()
fork()
s workersimport ddtrace.profiling.auto
and this leads all native extensions imported to the Python app. During the process, the constructors for global static variables are called and their destructors would be registered to be called by Catexit()
when the program exits.--lazy-apps
is set, ddtrace simply uses Python'satexit
module to callProfiler.stop()
when the program exits.uwsgi_python_atexit()
, they're called first.Py_Finalize()
is called in part ofuwsgi_python_atexit()
callingProfiler.stop()
. Then it will access those destructed global static strings leading to undefined behavior.To workaround this I have tried three different approaches.
std::string
from Python side to native when exporting pprof file for testThe first approach only addresses issues from tests.
The second approach mimics what's suggested in pytorch/pytorch#42125, but since there are a few other global static variables we have throughout the profiler, this still showed crashes from test.
And we got similar results from the third approach, as we did from the second.
Another way to completely work around this problem is setting
--skip-atexit
as suggested in ansible/ansible-ai-connect-service#248.When
--skip-atexit
is set,uwsgi
uses_exit()
instead ofexit()
, and the former doesn't call any functions registered withatexit()
oron_exit()
. So, the static variable destructors are never called, and the app no longer crashes.The profiler also doesn't flush the profile via
atexit()
when--skip-atexit
is set, but I'd argue losing a single profile at the end is better than crashing the application even if the application is exiting. So we'd need to require our customers to set--skip-atexit
when--lazy-apps
is set.Checklist
Reviewer Checklist