docs(uwsgi): update docs on --lazy-apps for uwsgi and tests#13550
Merged
Conversation
Contributor
|
|
nsrip-dd
reviewed
May 30, 2025
Contributor
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
wantsui
reviewed
Jun 2, 2025
wantsui
reviewed
Jun 3, 2025
wantsui
approved these changes
Jun 3, 2025
nsrip-dd
approved these changes
Jun 3, 2025
nsrip-dd
left a comment
Contributor
There was a problem hiding this comment.
Nice work fixing the problem upstream!
…trace-py into taegyunkim/uwsgi-atexit-hook
This was referenced Jun 3, 2025
2 tasks
taegyunkim
added a commit
that referenced
this pull request
Sep 18, 2025
… `--lazy-apps` (#14526) With this change, profiling won't start if they don't have `--skip-atexit` when either `--lazy` or `--lazy-apps` is set on `uWSGI<2.0.30`. This is a follow up to #13550 See details unbit/uwsgi#2726 We plan to change this behavior to throw `uWSGIConfigError` from 4.x releases. ## Checklist - [x] PR author has checked that all the criteria below are met - The PR description includes an overview of the change - The PR description articulates the motivation for the change - The change includes tests OR the PR description describes a testing strategy - The PR description notes risks associated with the change, if any - Newly-added code is easy to change - The change follows the [library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) - The change includes or references documentation updates if necessary - Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) ## Reviewer Checklist - [x] Reviewer has checked that all the criteria below are met - Title is accurate - All changes are related to the pull request's stated goal - Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes - Testing strategy adequately addresses listed risks - Newly-added code is easy to change - Release note makes sense to a user of the library - If necessary, author has acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment - Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
tl;dr
uwsgi>=2.0.30which has fix: install atexit handlers after apps are initialized unbit/uwsgi#2726uwsgi<2.0.30with--lazy-apps, also set--skip-atexit, if you see crashes when workers exit.The following explanation happens on
uwsgi<2.0.30When
uwsgiis 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
atexitmodule to export the profile when the process exits. The code run to export references global staticstd::stringvariables. However, when--lazy-appsoption is set, they are destructed before when the profiler needs them to export the profile, and this leads to an undefined behavior.When
--lazy-appsis not set the following sequence of events happensimport ddtrace.profiling.autoand 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 touwsgipython module'satexitattribute.uwsgicallsatexit(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-appsis setuwsgicallsatexit(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.autoand 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-appsis set, ddtrace simply uses Python'satexitmodule 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::stringfrom 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-atexitas suggested in ansible/ansible-ai-connect-service#248.When
--skip-atexitis set,uwsgiuses_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-atexitis 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-atexitwhen--lazy-appsis set.Checklist
Reviewer Checklist