Skip to content

refactor: native fork-safe threads#14163

Merged
brettlangdon merged 56 commits into
mainfrom
refactor/native-forksafe-threads
Feb 13, 2026
Merged

refactor: native fork-safe threads#14163
brettlangdon merged 56 commits into
mainfrom
refactor/native-forksafe-threads

Conversation

@P403n1x87

@P403n1x87 P403n1x87 commented Jul 29, 2025

Copy link
Copy Markdown
Contributor

We refactor the native periodic thread implementation to be fork-safe whereby all such threads that are running at the time of a fork are automatically stopped before the fork, then restarted after it. We also take care to avoid stopping and restarting threads in the parent process if we detect an immediate call to fork again.

Some of the implications of this change are that there is no longer the need for fork-safe synchronisation objects, such as Locks and Events. This is because all (ddtrace) threads are guaranteed to be stopped before a fork, and restarted afterwards. There is also no longer the need to manually recreate threads, as currently done in many places. The only thing that needs to be taken care of is to ensure that the state of the periodic services is as expected after a fork. For example, many products have the need to avoid sending duplicate values from different processes. This can be achieved by either subclassing from ForksafeAwakeablePeriodicService and implementing the reset method to let any periodic service to automatically trigger the reset logic on forks (the preferred way), or by the current approach of registering a fork-safe hook.

Performance Analysis

Stopping and restarting threads can be expensive but gives good fork-safety guarantees. Because threads have to be joined before the fork can continue, a forked process might get delayed by a thread currently busy on I/O. Because our periodic threads run on periods that are O(1s), we can expect these unfortunate event to be quite rare. Besides, it is quite likely that a process forks at the very beginning of its execution, where it is unlikely that the periodic threads have had a chance to trigger their first periodic action.

The timer set around close fork invocation should reduce delays even more by preventing the parent process from stopping and restarting threads in between fork calls. To give some number, the invocation of 1000 forks in a tight loop with the tracer and the profiler enabled require ~1.2s to complete on an M1. In comparison, the same loop without ddtrace takes 0.3s. The same loop with stop/restart in between forks takes ~1.5s.

The wall-time view through a profiler gives the following picture:

Screenshot 2025-07-29 at 11 27 49

A good portion of the overhead comes from the tracing of fork itself, which could be improved by moving it to the native layer in follow-up work.

Checklist

  • 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
  • The change includes or references documentation updates if necessary
  • Backport labels are set (if applicable)

Reviewer Checklist

  • 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 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

@github-actions

github-actions Bot commented Jul 29, 2025

Copy link
Copy Markdown
Contributor

CODEOWNERS have been resolved as:

ddtrace/internal/threads.py                                             @DataDog/apm-core-python
ddtrace/_trace/context.py                                               @DataDog/apm-sdk-capabilities-python
ddtrace/contrib/internal/ray/span_manager.py                            @DataDog/ml-observability
ddtrace/contrib/internal/subprocess/patch.py                            @DataDog/asm-python
ddtrace/debugging/_encoding.py                                          @DataDog/debugger-python
ddtrace/debugging/_origin/span.py                                       @DataDog/debugger-python
ddtrace/debugging/_probe/registry.py                                    @DataDog/debugger-python
ddtrace/internal/_threads.cpp                                           @DataDog/apm-core-python
ddtrace/internal/_threads.pyi                                           @DataDog/apm-core-python
ddtrace/internal/datadog/profiling/stack_v2/src/sampler.cpp             @DataDog/profiling-python
ddtrace/internal/datastreams/processor.py                               @DataDog/data-streams-monitoring
ddtrace/internal/forksafe.py                                            @DataDog/apm-core-python
ddtrace/internal/periodic.py                                            @DataDog/apm-core-python
ddtrace/internal/processor/endpoint_call_counter.py                     @DataDog/apm-core-python
ddtrace/internal/processor/stats.py                                     @DataDog/apm-core-python
ddtrace/internal/remoteconfig/worker.py                                 @DataDog/remote-config @DataDog/apm-core-python
ddtrace/internal/runtime/runtime_metrics.py                             @DataDog/apm-sdk-capabilities-python
ddtrace/internal/service.py                                             @DataDog/apm-core-python
ddtrace/internal/telemetry/metrics_namespaces.pyx                       @DataDog/apm-python
ddtrace/internal/telemetry/writer.py                                    @DataDog/apm-python
ddtrace/llmobs/_evaluators/runner.py                                    @DataDog/ml-observability
ddtrace/llmobs/_llmobs.py                                               @DataDog/ml-observability
ddtrace/llmobs/_log_writer.py                                           @DataDog/ml-observability
ddtrace/llmobs/_writer.py                                               @DataDog/ml-observability
ddtrace/profiling/collector/stack.pyx                                   @DataDog/profiling-python
ddtrace/profiling/profiler.py                                           @DataDog/profiling-python
ddtrace/profiling/scheduler.py                                          @DataDog/profiling-python
tests/contrib/uwsgi/__init__.py                                         @DataDog/apm-core-python @DataDog/apm-idm-python
tests/internal/service_name/test_extra_services_names.py                @DataDog/apm-core-python
tests/internal/test_forksafe.py                                         @DataDog/apm-core-python
tests/internal/test_periodic.py                                         @DataDog/apm-core-python
tests/profiling/collector/pprof_utils.py                                @DataDog/profiling-python
tests/profiling/gevent_fork.py                                          @DataDog/profiling-python
tests/profiling/test_main.py                                            @DataDog/profiling-python
tests/profiling/test_uwsgi.py                                           @DataDog/profiling-python
tests/profiling/uwsgi-app.py                                            @DataDog/profiling-python
tests/profiling_v2/test_gunicorn.py                                     @DataDog/profiling-python
tests/profiling_v2/test_main.py                                         @DataDog/profiling-python
tests/profiling_v2/test_uwsgi.py                                        @DataDog/profiling-python

@P403n1x87
P403n1x87 force-pushed the refactor/native-forksafe-threads branch from 7b63a8c to 8f151dd Compare July 29, 2025 10:49
@P403n1x87 P403n1x87 added the changelog/no-changelog A changelog entry is not required for this PR. label Jul 29, 2025
Comment thread ddtrace/internal/runtime/runtime_metrics.py
@pr-commenter

pr-commenter Bot commented Jul 29, 2025

Copy link
Copy Markdown

Performance SLOs

Comparing candidate refactor/native-forksafe-threads (881c153) with baseline main (3cc3eb7)

📈 Performance Regressions (2 suites)
📈 iastaspects - 118/118

✅ add_aspect

Time: ✅ 103.017µs (SLO: <130.000µs 📉 -20.8%) vs baseline: -2.2%

Memory: ✅ 42.939MB (SLO: <46.000MB -6.7%) vs baseline: +3.9%


✅ add_inplace_aspect

Time: ✅ 100.841µs (SLO: <130.000µs 📉 -22.4%) vs baseline: -4.2%

Memory: ✅ 42.861MB (SLO: <46.000MB -6.8%) vs baseline: +4.3%


✅ add_inplace_noaspect

Time: ✅ 28.382µs (SLO: <40.000µs 📉 -29.0%) vs baseline: +0.2%

Memory: ✅ 42.900MB (SLO: <46.000MB -6.7%) vs baseline: +4.2%


✅ add_noaspect

Time: ✅ 48.682µs (SLO: <70.000µs 📉 -30.5%) vs baseline: -1.2%

Memory: ✅ 42.920MB (SLO: <46.000MB -6.7%) vs baseline: +4.3%


✅ bytearray_aspect

Time: ✅ 251.087µs (SLO: <400.000µs 📉 -37.2%) vs baseline: +0.7%

Memory: ✅ 42.939MB (SLO: <46.000MB -6.7%) vs baseline: +4.3%


✅ bytearray_extend_aspect

Time: ✅ 635.720µs (SLO: <800.000µs 📉 -20.5%) vs baseline: -0.3%

Memory: ✅ 42.900MB (SLO: <46.000MB -6.7%) vs baseline: +4.2%


✅ bytearray_extend_noaspect

Time: ✅ 266.082µs (SLO: <400.000µs 📉 -33.5%) vs baseline: +0.5%

Memory: ✅ 42.900MB (SLO: <46.000MB -6.7%) vs baseline: +4.3%


✅ bytearray_noaspect

Time: ✅ 134.869µs (SLO: <300.000µs 📉 -55.0%) vs baseline: ~same

Memory: ✅ 42.900MB (SLO: <46.000MB -6.7%) vs baseline: +4.3%


✅ bytes_aspect

Time: ✅ 218.231µs (SLO: <300.000µs 📉 -27.3%) vs baseline: +0.8%

Memory: ✅ 42.841MB (SLO: <46.000MB -6.9%) vs baseline: +4.2%


✅ bytes_noaspect

Time: ✅ 133.334µs (SLO: <200.000µs 📉 -33.3%) vs baseline: +1.0%

Memory: ✅ 42.841MB (SLO: <46.000MB -6.9%) vs baseline: +4.1%


✅ bytesio_aspect

Time: ✅ 3.774ms (SLO: <5.000ms 📉 -24.5%) vs baseline: -0.9%

Memory: ✅ 42.959MB (SLO: <46.000MB -6.6%) vs baseline: +4.4%


✅ bytesio_noaspect

Time: ✅ 315.126µs (SLO: <420.000µs 📉 -25.0%) vs baseline: +0.1%

Memory: ✅ 42.880MB (SLO: <46.000MB -6.8%) vs baseline: +4.0%


✅ capitalize_aspect

Time: ✅ 89.561µs (SLO: <300.000µs 📉 -70.1%) vs baseline: +1.5%

Memory: ✅ 42.880MB (SLO: <46.000MB -6.8%) vs baseline: +4.1%


✅ capitalize_noaspect

Time: ✅ 252.176µs (SLO: <300.000µs 📉 -15.9%) vs baseline: +0.3%

Memory: ✅ 42.939MB (SLO: <46.000MB -6.7%) vs baseline: +4.3%


✅ casefold_aspect

Time: ✅ 89.016µs (SLO: <500.000µs 📉 -82.2%) vs baseline: +0.6%

Memory: ✅ 42.959MB (SLO: <46.000MB -6.6%) vs baseline: +4.3%


✅ casefold_noaspect

Time: ✅ 306.523µs (SLO: <500.000µs 📉 -38.7%) vs baseline: -0.1%

Memory: ✅ 42.900MB (SLO: <46.000MB -6.7%) vs baseline: +4.3%


✅ decode_aspect

Time: ✅ 86.931µs (SLO: <100.000µs 📉 -13.1%) vs baseline: ~same

Memory: ✅ 42.880MB (SLO: <46.000MB -6.8%) vs baseline: +4.3%


✅ decode_noaspect

Time: ✅ 152.010µs (SLO: <210.000µs 📉 -27.6%) vs baseline: +0.2%

Memory: ✅ 42.861MB (SLO: <46.000MB -6.8%) vs baseline: +4.1%


✅ encode_aspect

Time: ✅ 84.630µs (SLO: <200.000µs 📉 -57.7%) vs baseline: -0.4%

Memory: ✅ 42.841MB (SLO: <46.000MB -6.9%) vs baseline: +3.9%


✅ encode_noaspect

Time: ✅ 140.519µs (SLO: <200.000µs 📉 -29.7%) vs baseline: +2.2%

Memory: ✅ 42.900MB (SLO: <46.000MB -6.7%) vs baseline: +4.1%


✅ format_aspect

Time: ✅ 14.613ms (SLO: <19.200ms 📉 -23.9%) vs baseline: +0.1%

Memory: ✅ 43.175MB (SLO: <46.000MB -6.1%) vs baseline: +4.7%


✅ format_map_aspect

Time: ✅ 16.416ms (SLO: <21.500ms 📉 -23.6%) vs baseline: +0.4%

Memory: ✅ 42.979MB (SLO: <46.000MB -6.6%) vs baseline: +4.3%


✅ format_map_noaspect

Time: ✅ 374.539µs (SLO: <500.000µs 📉 -25.1%) vs baseline: -0.2%

Memory: ✅ 42.900MB (SLO: <46.000MB -6.7%) vs baseline: +4.2%


✅ format_noaspect

Time: ✅ 302.943µs (SLO: <500.000µs 📉 -39.4%) vs baseline: -0.5%

Memory: ✅ 42.802MB (SLO: <46.000MB -7.0%) vs baseline: +4.3%


✅ index_aspect

Time: ✅ 123.744µs (SLO: <300.000µs 📉 -58.8%) vs baseline: +1.2%

Memory: ✅ 42.880MB (SLO: <46.000MB -6.8%) vs baseline: +4.2%


✅ index_noaspect

Time: ✅ 40.420µs (SLO: <300.000µs 📉 -86.5%) vs baseline: +1.0%

Memory: ✅ 42.900MB (SLO: <46.000MB -6.7%) vs baseline: +4.5%


✅ join_aspect

Time: ✅ 212.427µs (SLO: <300.000µs 📉 -29.2%) vs baseline: +0.3%

Memory: ✅ 42.920MB (SLO: <46.000MB -6.7%) vs baseline: +4.6%


✅ join_noaspect

Time: ✅ 140.596µs (SLO: <300.000µs 📉 -53.1%) vs baseline: -1.6%

Memory: ✅ 42.920MB (SLO: <46.000MB -6.7%) vs baseline: +4.3%


✅ ljust_aspect

Time: ✅ 577.954µs (SLO: <700.000µs 📉 -17.4%) vs baseline: 📈 +16.5%

Memory: ✅ 42.920MB (SLO: <46.000MB -6.7%) vs baseline: +4.0%


✅ ljust_noaspect

Time: ✅ 258.335µs (SLO: <300.000µs 📉 -13.9%) vs baseline: +0.9%

Memory: ✅ 42.959MB (SLO: <46.000MB -6.6%) vs baseline: +4.4%


✅ lower_aspect

Time: ✅ 293.979µs (SLO: <500.000µs 📉 -41.2%) vs baseline: +0.3%

Memory: ✅ 42.880MB (SLO: <46.000MB -6.8%) vs baseline: +4.4%


✅ lower_noaspect

Time: ✅ 236.210µs (SLO: <300.000µs 📉 -21.3%) vs baseline: +0.6%

Memory: ✅ 42.939MB (SLO: <46.000MB -6.7%) vs baseline: +4.4%


✅ lstrip_aspect

Time: ✅ 0.272ms (SLO: <3.000ms 📉 -90.9%) vs baseline: +0.9%

Memory: ✅ 42.841MB (SLO: <46.000MB -6.9%) vs baseline: +4.0%


✅ lstrip_noaspect

Time: ✅ 0.178ms (SLO: <3.000ms 📉 -94.1%) vs baseline: +1.6%

Memory: ✅ 42.939MB (SLO: <46.000MB -6.7%) vs baseline: +4.3%


✅ modulo_aspect

Time: ✅ 14.363ms (SLO: <18.750ms 📉 -23.4%) vs baseline: +0.7%

Memory: ✅ 42.979MB (SLO: <46.000MB -6.6%) vs baseline: +4.2%


✅ modulo_aspect_for_bytearray_bytearray

Time: ✅ 14.778ms (SLO: <19.350ms 📉 -23.6%) vs baseline: +0.3%

Memory: ✅ 42.959MB (SLO: <46.000MB -6.6%) vs baseline: +4.2%


✅ modulo_aspect_for_bytes

Time: ✅ 14.390ms (SLO: <18.900ms 📉 -23.9%) vs baseline: -0.2%

Memory: ✅ 42.959MB (SLO: <46.000MB -6.6%) vs baseline: +4.2%


✅ modulo_aspect_for_bytes_bytearray

Time: ✅ 14.571ms (SLO: <19.150ms 📉 -23.9%) vs baseline: +0.3%

Memory: ✅ 42.998MB (SLO: <46.000MB -6.5%) vs baseline: +4.5%


✅ modulo_noaspect

Time: ✅ 0.360ms (SLO: <3.000ms 📉 -88.0%) vs baseline: -1.6%

Memory: ✅ 42.880MB (SLO: <46.000MB -6.8%) vs baseline: +4.3%


✅ replace_aspect

Time: ✅ 18.398ms (SLO: <24.000ms 📉 -23.3%) vs baseline: -0.3%

Memory: ✅ 42.979MB (SLO: <46.000MB -6.6%) vs baseline: +4.4%


✅ replace_noaspect

Time: ✅ 281.567µs (SLO: <300.000µs -6.1%) vs baseline: +1.2%

Memory: ✅ 42.939MB (SLO: <46.000MB -6.7%) vs baseline: +4.5%


✅ repr_aspect

Time: ✅ 312.325µs (SLO: <420.000µs 📉 -25.6%) vs baseline: +0.6%

Memory: ✅ 42.920MB (SLO: <46.000MB -6.7%) vs baseline: +4.2%


✅ repr_noaspect

Time: ✅ 47.073µs (SLO: <90.000µs 📉 -47.7%) vs baseline: -0.2%

Memory: ✅ 42.979MB (SLO: <46.000MB -6.6%) vs baseline: +4.6%


✅ rstrip_aspect

Time: ✅ 383.545µs (SLO: <500.000µs 📉 -23.3%) vs baseline: +0.5%

Memory: ✅ 42.880MB (SLO: <46.000MB -6.8%) vs baseline: +4.1%


✅ rstrip_noaspect

Time: ✅ 184.303µs (SLO: <300.000µs 📉 -38.6%) vs baseline: +2.3%

Memory: ✅ 42.920MB (SLO: <46.000MB -6.7%) vs baseline: +4.2%


✅ slice_aspect

Time: ✅ 185.036µs (SLO: <300.000µs 📉 -38.3%) vs baseline: +1.4%

Memory: ✅ 42.880MB (SLO: <46.000MB -6.8%) vs baseline: +4.4%


✅ slice_noaspect

Time: ✅ 54.491µs (SLO: <90.000µs 📉 -39.5%) vs baseline: +1.0%

Memory: ✅ 42.939MB (SLO: <46.000MB -6.7%) vs baseline: +4.3%


✅ stringio_aspect

Time: ✅ 4.407ms (SLO: <5.000ms 📉 -11.9%) vs baseline: 📈 +14.4%

Memory: ✅ 42.979MB (SLO: <46.000MB -6.6%) vs baseline: +4.4%


✅ stringio_noaspect

Time: ✅ 346.454µs (SLO: <500.000µs 📉 -30.7%) vs baseline: -0.5%

Memory: ✅ 42.821MB (SLO: <46.000MB -6.9%) vs baseline: +4.0%


✅ strip_aspect

Time: ✅ 269.112µs (SLO: <350.000µs 📉 -23.1%) vs baseline: -1.1%

Memory: ✅ 42.959MB (SLO: <46.000MB -6.6%) vs baseline: +4.5%


✅ strip_noaspect

Time: ✅ 176.534µs (SLO: <240.000µs 📉 -26.4%) vs baseline: +1.2%

Memory: ✅ 42.900MB (SLO: <46.000MB -6.7%) vs baseline: +4.2%


✅ swapcase_aspect

Time: ✅ 333.976µs (SLO: <500.000µs 📉 -33.2%) vs baseline: +0.5%

Memory: ✅ 42.880MB (SLO: <46.000MB -6.8%) vs baseline: +4.4%


✅ swapcase_noaspect

Time: ✅ 270.936µs (SLO: <400.000µs 📉 -32.3%) vs baseline: -0.4%

Memory: ✅ 42.998MB (SLO: <46.000MB -6.5%) vs baseline: +4.5%


✅ title_aspect

Time: ✅ 319.943µs (SLO: <500.000µs 📉 -36.0%) vs baseline: -0.1%

Memory: ✅ 42.900MB (SLO: <46.000MB -6.7%) vs baseline: +4.3%


✅ title_noaspect

Time: ✅ 256.967µs (SLO: <400.000µs 📉 -35.8%) vs baseline: -2.2%

Memory: ✅ 42.920MB (SLO: <46.000MB -6.7%) vs baseline: +4.1%


✅ translate_aspect

Time: ✅ 492.924µs (SLO: <700.000µs 📉 -29.6%) vs baseline: +0.4%

Memory: ✅ 42.861MB (SLO: <46.000MB -6.8%) vs baseline: +4.2%


✅ translate_noaspect

Time: ✅ 424.654µs (SLO: <500.000µs 📉 -15.1%) vs baseline: -0.5%

Memory: ✅ 42.920MB (SLO: <46.000MB -6.7%) vs baseline: +4.4%


✅ upper_aspect

Time: ✅ 294.840µs (SLO: <500.000µs 📉 -41.0%) vs baseline: +0.6%

Memory: ✅ 42.959MB (SLO: <46.000MB -6.6%) vs baseline: +4.5%


✅ upper_noaspect

Time: ✅ 235.633µs (SLO: <400.000µs 📉 -41.1%) vs baseline: +0.6%

Memory: ✅ 42.880MB (SLO: <46.000MB -6.8%) vs baseline: +4.3%


📈 iastaspectsospath - 24/24

✅ ospathbasename_aspect

Time: ✅ 507.776µs (SLO: <700.000µs 📉 -27.5%) vs baseline: 📈 +21.7%

Memory: ✅ 42.585MB (SLO: <46.000MB -7.4%) vs baseline: +4.1%


✅ ospathbasename_noaspect

Time: ✅ 432.158µs (SLO: <700.000µs 📉 -38.3%) vs baseline: +0.7%

Memory: ✅ 42.644MB (SLO: <46.000MB -7.3%) vs baseline: +4.7%


✅ ospathjoin_aspect

Time: ✅ 627.353µs (SLO: <700.000µs 📉 -10.4%) vs baseline: +0.9%

Memory: ✅ 42.566MB (SLO: <46.000MB -7.5%) vs baseline: +4.3%


✅ ospathjoin_noaspect

Time: ✅ 631.404µs (SLO: <700.000µs -9.8%) vs baseline: -0.3%

Memory: ✅ 42.566MB (SLO: <46.000MB -7.5%) vs baseline: +4.4%


✅ ospathnormcase_aspect

Time: ✅ 348.248µs (SLO: <700.000µs 📉 -50.3%) vs baseline: +1.2%

Memory: ✅ 42.507MB (SLO: <46.000MB -7.6%) vs baseline: +4.3%


✅ ospathnormcase_noaspect

Time: ✅ 357.185µs (SLO: <700.000µs 📉 -49.0%) vs baseline: +0.6%

Memory: ✅ 42.723MB (SLO: <46.000MB -7.1%) vs baseline: +4.9%


✅ ospathsplit_aspect

Time: ✅ 486.752µs (SLO: <700.000µs 📉 -30.5%) vs baseline: +1.2%

Memory: ✅ 42.546MB (SLO: <46.000MB -7.5%) vs baseline: +4.5%


✅ ospathsplit_noaspect

Time: ✅ 498.017µs (SLO: <700.000µs 📉 -28.9%) vs baseline: +1.5%

Memory: ✅ 42.585MB (SLO: <46.000MB -7.4%) vs baseline: +4.1%


✅ ospathsplitdrive_aspect

Time: ✅ 376.190µs (SLO: <700.000µs 📉 -46.3%) vs baseline: +1.5%

Memory: ✅ 42.566MB (SLO: <46.000MB -7.5%) vs baseline: +4.5%


✅ ospathsplitdrive_noaspect

Time: ✅ 72.831µs (SLO: <700.000µs 📉 -89.6%) vs baseline: -0.4%

Memory: ✅ 42.546MB (SLO: <46.000MB -7.5%) vs baseline: +4.2%


✅ ospathsplitext_aspect

Time: ✅ 456.264µs (SLO: <700.000µs 📉 -34.8%) vs baseline: +2.0%

Memory: ✅ 42.605MB (SLO: <46.000MB -7.4%) vs baseline: +4.6%


✅ ospathsplitext_noaspect

Time: ✅ 463.925µs (SLO: <700.000µs 📉 -33.7%) vs baseline: +1.3%

Memory: ✅ 42.605MB (SLO: <46.000MB -7.4%) vs baseline: +4.2%

🟡 Near SLO Breach (1 suite)
🟡 tracer - 6/6

✅ large

Time: ✅ 31.786ms (SLO: <32.950ms -3.5%) vs baseline: +0.6%

Memory: ✅ 36.766MB (SLO: <39.250MB -6.3%) vs baseline: +4.4%


✅ medium

Time: ✅ 3.129ms (SLO: <3.200ms -2.2%) vs baseline: ~same

Memory: ✅ 35.547MB (SLO: <38.750MB -8.3%) vs baseline: +4.4%


✅ small

Time: ✅ 363.966µs (SLO: <370.000µs 🟡 -1.6%) vs baseline: +3.1%

Memory: ✅ 35.488MB (SLO: <38.750MB -8.4%) vs baseline: +4.5%

📉 Performance Improvements (2 suites)
📉 httppropagationextract - 60/60

✅ all_styles_all_headers

Time: ✅ 79.572µs (SLO: <100.000µs 📉 -20.4%) vs baseline: -1.3%

Memory: ✅ 35.625MB (SLO: <38.000MB -6.2%) vs baseline: +4.2%


✅ b3_headers

Time: ✅ 12.770µs (SLO: <20.000µs 📉 -36.2%) vs baseline: 📉 -10.4%

Memory: ✅ 35.665MB (SLO: <38.000MB -6.1%) vs baseline: +4.4%


✅ b3_single_headers

Time: ✅ 11.819µs (SLO: <20.000µs 📉 -40.9%) vs baseline: 📉 -11.0%

Memory: ✅ 35.527MB (SLO: <38.000MB -6.5%) vs baseline: +4.0%


✅ datadog_tracecontext_tracestate_not_propagated_on_trace_id_no_match

Time: ✅ 60.705µs (SLO: <80.000µs 📉 -24.1%) vs baseline: -5.4%

Memory: ✅ 35.606MB (SLO: <38.000MB -6.3%) vs baseline: +4.2%


✅ datadog_tracecontext_tracestate_propagated_on_trace_id_match

Time: ✅ 62.553µs (SLO: <80.000µs 📉 -21.8%) vs baseline: -5.3%

Memory: ✅ 35.566MB (SLO: <38.000MB -6.4%) vs baseline: +4.2%


✅ empty_headers

Time: ✅ 1.304µs (SLO: <10.000µs 📉 -87.0%) vs baseline: 📉 -17.5%

Memory: ✅ 35.625MB (SLO: <38.000MB -6.2%) vs baseline: +4.5%


✅ full_t_id_datadog_headers

Time: ✅ 20.848µs (SLO: <30.000µs 📉 -30.5%) vs baseline: -8.0%

Memory: ✅ 35.645MB (SLO: <38.000MB -6.2%) vs baseline: +4.5%


✅ invalid_priority_header

Time: ✅ 5.928µs (SLO: <10.000µs 📉 -40.7%) vs baseline: -8.9%

Memory: ✅ 35.684MB (SLO: <38.000MB -6.1%) vs baseline: +4.7%


✅ invalid_span_id_header

Time: ✅ 5.873µs (SLO: <10.000µs 📉 -41.3%) vs baseline: -9.7%

Memory: ✅ 35.606MB (SLO: <38.000MB -6.3%) vs baseline: +4.5%


✅ invalid_tags_header

Time: ✅ 5.869µs (SLO: <10.000µs 📉 -41.3%) vs baseline: -10.0%

Memory: ✅ 35.684MB (SLO: <38.000MB -6.1%) vs baseline: +4.5%


✅ invalid_trace_id_header

Time: ✅ 5.892µs (SLO: <10.000µs 📉 -41.1%) vs baseline: 📉 -10.2%

Memory: ✅ 35.724MB (SLO: <38.000MB -6.0%) vs baseline: +4.7%


✅ large_header_no_matches

Time: ✅ 27.001µs (SLO: <30.000µs -10.0%) vs baseline: -2.4%

Memory: ✅ 35.704MB (SLO: <38.000MB -6.0%) vs baseline: +4.5%


✅ large_valid_headers_all

Time: ✅ 28.240µs (SLO: <40.000µs 📉 -29.4%) vs baseline: -1.6%

Memory: ✅ 35.743MB (SLO: <38.000MB -5.9%) vs baseline: +4.7%


✅ medium_header_no_matches

Time: ✅ 9.204µs (SLO: <20.000µs 📉 -54.0%) vs baseline: -6.4%

Memory: ✅ 35.724MB (SLO: <38.000MB -6.0%) vs baseline: +4.5%


✅ medium_valid_headers_all

Time: ✅ 10.695µs (SLO: <20.000µs 📉 -46.5%) vs baseline: -5.3%

Memory: ✅ 35.586MB (SLO: <38.000MB -6.4%) vs baseline: +4.1%


✅ none_propagation_style

Time: ✅ 1.406µs (SLO: <10.000µs 📉 -85.9%) vs baseline: 📉 -17.2%

Memory: ✅ 35.665MB (SLO: <38.000MB -6.1%) vs baseline: +4.6%


✅ tracecontext_headers

Time: ✅ 32.573µs (SLO: <40.000µs 📉 -18.6%) vs baseline: -6.0%

Memory: ✅ 35.547MB (SLO: <38.000MB -6.5%) vs baseline: +4.2%


✅ valid_headers_all

Time: ✅ 5.875µs (SLO: <10.000µs 📉 -41.2%) vs baseline: -9.3%

Memory: ✅ 35.665MB (SLO: <38.000MB -6.1%) vs baseline: +4.5%


✅ valid_headers_basic

Time: ✅ 5.473µs (SLO: <10.000µs 📉 -45.3%) vs baseline: -10.0%

Memory: ✅ 35.527MB (SLO: <38.000MB -6.5%) vs baseline: +4.1%


✅ wsgi_empty_headers

Time: ✅ 1.305µs (SLO: <10.000µs 📉 -87.0%) vs baseline: 📉 -18.0%

Memory: ✅ 35.724MB (SLO: <38.000MB -6.0%) vs baseline: +4.7%


✅ wsgi_invalid_priority_header

Time: ✅ 5.933µs (SLO: <10.000µs 📉 -40.7%) vs baseline: -9.6%

Memory: ✅ 35.547MB (SLO: <38.000MB -6.5%) vs baseline: +4.3%


✅ wsgi_invalid_span_id_header

Time: ✅ 1.298µs (SLO: <10.000µs 📉 -87.0%) vs baseline: 📉 -18.9%

Memory: ✅ 35.645MB (SLO: <38.000MB -6.2%) vs baseline: +4.2%


✅ wsgi_invalid_tags_header

Time: ✅ 5.941µs (SLO: <10.000µs 📉 -40.6%) vs baseline: -9.7%

Memory: ✅ 35.625MB (SLO: <38.000MB -6.2%) vs baseline: +4.3%


✅ wsgi_invalid_trace_id_header

Time: ✅ 5.945µs (SLO: <10.000µs 📉 -40.5%) vs baseline: -9.4%

Memory: ✅ 35.606MB (SLO: <38.000MB -6.3%) vs baseline: +4.2%


✅ wsgi_large_header_no_matches

Time: ✅ 28.034µs (SLO: <40.000µs 📉 -29.9%) vs baseline: -2.3%

Memory: ✅ 35.606MB (SLO: <38.000MB -6.3%) vs baseline: +4.3%


✅ wsgi_large_valid_headers_all

Time: ✅ 29.131µs (SLO: <40.000µs 📉 -27.2%) vs baseline: -2.7%

Memory: ✅ 35.665MB (SLO: <38.000MB -6.1%) vs baseline: +4.5%


✅ wsgi_medium_header_no_matches

Time: ✅ 9.418µs (SLO: <20.000µs 📉 -52.9%) vs baseline: -7.4%

Memory: ✅ 35.645MB (SLO: <38.000MB -6.2%) vs baseline: +4.6%


✅ wsgi_medium_valid_headers_all

Time: ✅ 11.048µs (SLO: <20.000µs 📉 -44.8%) vs baseline: -4.1%

Memory: ✅ 35.566MB (SLO: <38.000MB -6.4%) vs baseline: +4.1%


✅ wsgi_valid_headers_all

Time: ✅ 5.938µs (SLO: <10.000µs 📉 -40.6%) vs baseline: -9.4%

Memory: ✅ 35.547MB (SLO: <38.000MB -6.5%) vs baseline: +3.9%


✅ wsgi_valid_headers_basic

Time: ✅ 5.513µs (SLO: <10.000µs 📉 -44.9%) vs baseline: -9.6%

Memory: ✅ 35.566MB (SLO: <38.000MB -6.4%) vs baseline: +4.1%


📉 telemetryaddmetric - 30/30

✅ 1-count-metric-1-times

Time: ✅ 2.258µs (SLO: <20.000µs 📉 -88.7%) vs baseline: 📉 -23.6%

Memory: ✅ 35.566MB (SLO: <38.000MB -6.4%) vs baseline: +4.3%


✅ 1-count-metrics-100-times

Time: ✅ 149.006µs (SLO: <220.000µs 📉 -32.3%) vs baseline: 📉 -27.1%

Memory: ✅ 35.468MB (SLO: <38.000MB -6.7%) vs baseline: +3.9%


✅ 1-distribution-metric-1-times

Time: ✅ 2.472µs (SLO: <20.000µs 📉 -87.6%) vs baseline: 📉 -24.9%

Memory: ✅ 35.586MB (SLO: <38.000MB -6.4%) vs baseline: +4.5%


✅ 1-distribution-metrics-100-times

Time: ✅ 164.557µs (SLO: <230.000µs 📉 -28.5%) vs baseline: 📉 -23.1%

Memory: ✅ 35.547MB (SLO: <38.000MB -6.5%) vs baseline: +4.1%


✅ 1-gauge-metric-1-times

Time: ✅ 1.956µs (SLO: <20.000µs 📉 -90.2%) vs baseline: 📉 -11.0%

Memory: ✅ 35.547MB (SLO: <38.000MB -6.5%) vs baseline: +4.3%


✅ 1-gauge-metrics-100-times

Time: ✅ 135.484µs (SLO: <150.000µs -9.7%) vs baseline: -1.7%

Memory: ✅ 35.527MB (SLO: <38.000MB -6.5%) vs baseline: +4.3%


✅ 1-rate-metric-1-times

Time: ✅ 2.222µs (SLO: <20.000µs 📉 -88.9%) vs baseline: 📉 -27.8%

Memory: ✅ 35.586MB (SLO: <38.000MB -6.4%) vs baseline: +4.6%


✅ 1-rate-metrics-100-times

Time: ✅ 161.951µs (SLO: <250.000µs 📉 -35.2%) vs baseline: 📉 -25.6%

Memory: ✅ 35.507MB (SLO: <38.000MB -6.6%) vs baseline: +4.1%


✅ 100-count-metrics-100-times

Time: ✅ 15.193ms (SLO: <22.000ms 📉 -30.9%) vs baseline: 📉 -25.3%

Memory: ✅ 35.704MB (SLO: <38.000MB -6.0%) vs baseline: +4.7%


✅ 100-distribution-metrics-100-times

Time: ✅ 1.748ms (SLO: <2.550ms 📉 -31.5%) vs baseline: 📉 -22.6%

Memory: ✅ 35.665MB (SLO: <38.000MB -6.1%) vs baseline: +4.7%


✅ 100-gauge-metrics-100-times

Time: ✅ 1.402ms (SLO: <1.550ms -9.6%) vs baseline: -0.3%

Memory: ✅ 35.625MB (SLO: <38.000MB -6.2%) vs baseline: +4.9%


✅ 100-rate-metrics-100-times

Time: ✅ 1.706ms (SLO: <2.550ms 📉 -33.1%) vs baseline: 📉 -23.3%

Memory: ✅ 35.547MB (SLO: <38.000MB -6.5%) vs baseline: +4.6%


✅ flush-1-metric

Time: ✅ 3.582µs (SLO: <20.000µs 📉 -82.1%) vs baseline: 📉 -18.7%

Memory: ✅ 35.547MB (SLO: <38.000MB -6.5%) vs baseline: +4.1%


✅ flush-100-metrics

Time: ✅ 174.506µs (SLO: <250.000µs 📉 -30.2%) vs baseline: ~same

Memory: ✅ 35.586MB (SLO: <38.000MB -6.4%) vs baseline: +4.5%


✅ flush-1000-metrics

Time: ✅ 2.192ms (SLO: <2.500ms 📉 -12.3%) vs baseline: +0.4%

Memory: ✅ 36.392MB (SLO: <38.750MB -6.1%) vs baseline: +4.6%

⚠️ Unstable Tests (2 suites)
⚠️ coreapiscenario - 10/10 (1 unstable)

⚠️ context_with_data_listeners

Time: ⚠️ 13.230µs (SLO: <20.000µs 📉 -33.9%) vs baseline: -0.8%

Memory: ✅ 35.527MB (SLO: <38.000MB -6.5%) vs baseline: +4.5%


✅ context_with_data_no_listeners

Time: ✅ 3.277µs (SLO: <10.000µs 📉 -67.2%) vs baseline: +0.5%

Memory: ✅ 35.547MB (SLO: <38.000MB -6.5%) vs baseline: +4.3%


✅ get_item_exists

Time: ✅ 0.578µs (SLO: <10.000µs 📉 -94.2%) vs baseline: -0.9%

Memory: ✅ 35.527MB (SLO: <38.000MB -6.5%) vs baseline: +4.6%


✅ get_item_missing

Time: ✅ 0.637µs (SLO: <10.000µs 📉 -93.6%) vs baseline: ~same

Memory: ✅ 35.586MB (SLO: <38.000MB -6.4%) vs baseline: +4.5%


✅ set_item

Time: ✅ 23.848µs (SLO: <30.000µs 📉 -20.5%) vs baseline: -0.9%

Memory: ✅ 35.468MB (SLO: <38.000MB -6.7%) vs baseline: +4.4%


⚠️ packagesupdateimporteddependencies - 24/24 (1 unstable)

✅ import_many

Time: ✅ 154.882µs (SLO: <170.000µs -8.9%) vs baseline: -0.5%

Memory: ✅ 40.020MB (SLO: <46.000MB 📉 -13.0%) vs baseline: +4.2%


✅ import_many_cached

Time: ✅ 120.470µs (SLO: <130.000µs -7.3%) vs baseline: -0.6%

Memory: ✅ 40.053MB (SLO: <46.000MB 📉 -12.9%) vs baseline: +4.9%


✅ import_many_stdlib

Time: ✅ 0.768ms (SLO: <1.750ms 📉 -56.1%) vs baseline: +0.4%

Memory: ✅ 40.135MB (SLO: <46.000MB 📉 -12.7%) vs baseline: +4.7%


⚠️ import_many_stdlib_cached

Time: ⚠️ 0.175ms (SLO: <1.100ms 📉 -84.1%) vs baseline: +1.4%

Memory: ✅ 40.101MB (SLO: <46.000MB 📉 -12.8%) vs baseline: +4.8%


✅ import_many_unknown

Time: ✅ 831.086µs (SLO: <890.000µs -6.6%) vs baseline: +0.3%

Memory: ✅ 40.311MB (SLO: <46.000MB 📉 -12.4%) vs baseline: +5.2%


✅ import_many_unknown_cached

Time: ✅ 802.231µs (SLO: <870.000µs -7.8%) vs baseline: +1.1%

Memory: ✅ 40.223MB (SLO: <46.000MB 📉 -12.6%) vs baseline: +4.6%


✅ import_one

Time: ✅ 19.804µs (SLO: <30.000µs 📉 -34.0%) vs baseline: -0.8%

Memory: ✅ 39.993MB (SLO: <46.000MB 📉 -13.1%) vs baseline: +4.2%


✅ import_one_cache

Time: ✅ 6.279µs (SLO: <10.000µs 📉 -37.2%) vs baseline: ~same

Memory: ✅ 40.267MB (SLO: <46.000MB 📉 -12.5%) vs baseline: +5.1%


✅ import_one_stdlib

Time: ✅ 18.735µs (SLO: <20.000µs -6.3%) vs baseline: ~same

Memory: ✅ 40.085MB (SLO: <46.000MB 📉 -12.9%) vs baseline: +4.8%


✅ import_one_stdlib_cache

Time: ✅ 6.264µs (SLO: <10.000µs 📉 -37.4%) vs baseline: -0.4%

Memory: ✅ 40.200MB (SLO: <46.000MB 📉 -12.6%) vs baseline: +5.4%


✅ import_one_unknown

Time: ✅ 45.752µs (SLO: <50.000µs -8.5%) vs baseline: -0.2%

Memory: ✅ 39.909MB (SLO: <46.000MB 📉 -13.2%) vs baseline: +4.4%


✅ import_one_unknown_cache

Time: ✅ 6.290µs (SLO: <10.000µs 📉 -37.1%) vs baseline: ~same

Memory: ✅ 40.067MB (SLO: <43.000MB -6.8%) vs baseline: +4.5%

✅ All Tests Passing (17 suites)
djangosimple - 30/30

✅ appsec

Time: ✅ 19.612ms (SLO: <22.300ms 📉 -12.1%) vs baseline: +0.3%

Memory: ✅ 69.145MB (SLO: <73.500MB -5.9%) vs baseline: +4.7%


✅ exception-replay-enabled

Time: ✅ 1.384ms (SLO: <1.450ms -4.6%) vs baseline: ~same

Memory: ✅ 67.425MB (SLO: <71.500MB -5.7%) vs baseline: +5.1%


✅ iast

Time: ✅ 19.622ms (SLO: <22.250ms 📉 -11.8%) vs baseline: ~same

Memory: ✅ 69.123MB (SLO: <75.000MB -7.8%) vs baseline: +4.6%


✅ profiler

Time: ✅ 15.206ms (SLO: <16.550ms -8.1%) vs baseline: -0.3%

Memory: ✅ 56.938MB (SLO: <61.000MB -6.7%) vs baseline: +4.9%


✅ resource-renaming

Time: ✅ 19.526ms (SLO: <21.750ms 📉 -10.2%) vs baseline: -0.2%

Memory: ✅ 69.176MB (SLO: <73.500MB -5.9%) vs baseline: +4.8%


✅ span-code-origin

Time: ✅ 19.969ms (SLO: <28.200ms 📉 -29.2%) vs baseline: +0.7%

Memory: ✅ 69.188MB (SLO: <75.000MB -7.7%) vs baseline: +4.9%


✅ tracer

Time: ✅ 19.617ms (SLO: <21.750ms -9.8%) vs baseline: +0.2%

Memory: ✅ 69.168MB (SLO: <75.000MB -7.8%) vs baseline: +4.8%


✅ tracer-and-profiler

Time: ✅ 21.123ms (SLO: <23.500ms 📉 -10.1%) vs baseline: -1.9%

Memory: ✅ 70.712MB (SLO: <75.000MB -5.7%) vs baseline: +5.0%


✅ tracer-dont-create-db-spans

Time: ✅ 19.772ms (SLO: <21.500ms -8.0%) vs baseline: +0.9%

Memory: ✅ 69.105MB (SLO: <75.000MB -7.9%) vs baseline: +4.6%


✅ tracer-minimal

Time: ✅ 16.751ms (SLO: <17.500ms -4.3%) vs baseline: -0.6%

Memory: ✅ 69.148MB (SLO: <75.000MB -7.8%) vs baseline: +4.8%


✅ tracer-native

Time: ✅ 19.553ms (SLO: <21.750ms 📉 -10.1%) vs baseline: +0.2%

Memory: ✅ 69.134MB (SLO: <72.500MB -4.6%) vs baseline: +4.7%


✅ tracer-no-caches

Time: ✅ 17.678ms (SLO: <19.650ms 📉 -10.0%) vs baseline: +0.7%

Memory: ✅ 69.142MB (SLO: <75.000MB -7.8%) vs baseline: +4.9%


✅ tracer-no-databases

Time: ✅ 19.154ms (SLO: <20.100ms -4.7%) vs baseline: -0.5%

Memory: ✅ 69.129MB (SLO: <75.000MB -7.8%) vs baseline: +4.7%


✅ tracer-no-middleware

Time: ✅ 19.390ms (SLO: <21.500ms -9.8%) vs baseline: +0.1%

Memory: ✅ 69.121MB (SLO: <75.000MB -7.8%) vs baseline: +4.6%


✅ tracer-no-templates

Time: ✅ 19.621ms (SLO: <22.000ms 📉 -10.8%) vs baseline: +1.5%

Memory: ✅ 69.078MB (SLO: <73.500MB -6.0%) vs baseline: +4.7%


errortrackingdjangosimple - 6/6

✅ errortracking-enabled-all

Time: ✅ 16.340ms (SLO: <19.850ms 📉 -17.7%) vs baseline: -0.5%

Memory: ✅ 68.687MB (SLO: <75.000MB -8.4%) vs baseline: +4.8%


✅ errortracking-enabled-user

Time: ✅ 16.321ms (SLO: <19.400ms 📉 -15.9%) vs baseline: -0.1%

Memory: ✅ 68.693MB (SLO: <75.000MB -8.4%) vs baseline: +5.0%


✅ tracer-enabled

Time: ✅ 16.389ms (SLO: <19.450ms 📉 -15.7%) vs baseline: +0.3%

Memory: ✅ 68.669MB (SLO: <75.000MB -8.4%) vs baseline: +4.9%


errortrackingflasksqli - 6/6

✅ errortracking-enabled-all

Time: ✅ 2.107ms (SLO: <2.300ms -8.4%) vs baseline: -0.2%

Memory: ✅ 56.525MB (SLO: <60.000MB -5.8%) vs baseline: +4.7%


✅ errortracking-enabled-user

Time: ✅ 2.118ms (SLO: <2.250ms -5.9%) vs baseline: ~same

Memory: ✅ 56.564MB (SLO: <60.000MB -5.7%) vs baseline: +4.6%


✅ tracer-enabled

Time: ✅ 2.108ms (SLO: <2.300ms -8.3%) vs baseline: ~same

Memory: ✅ 56.505MB (SLO: <60.000MB -5.8%) vs baseline: +4.7%


flasksimple - 18/18

✅ appsec-get

Time: ✅ 3.415ms (SLO: <4.750ms 📉 -28.1%) vs baseline: -0.4%

Memory: ✅ 56.231MB (SLO: <66.500MB 📉 -15.4%) vs baseline: +4.6%


✅ appsec-post

Time: ✅ 2.897ms (SLO: <6.750ms 📉 -57.1%) vs baseline: +0.2%

Memory: ✅ 56.623MB (SLO: <66.500MB 📉 -14.9%) vs baseline: +4.6%


✅ appsec-telemetry

Time: ✅ 3.452ms (SLO: <4.750ms 📉 -27.3%) vs baseline: +0.9%

Memory: ✅ 56.210MB (SLO: <66.500MB 📉 -15.5%) vs baseline: +4.5%


✅ debugger

Time: ✅ 1.867ms (SLO: <2.000ms -6.6%) vs baseline: ~same

Memory: ✅ 48.409MB (SLO: <51.500MB -6.0%) vs baseline: +3.9%


✅ iast-get

Time: ✅ 1.858ms (SLO: <2.000ms -7.1%) vs baseline: -0.2%

Memory: ✅ 45.339MB (SLO: <49.000MB -7.5%) vs baseline: +4.6%


✅ profiler

Time: ✅ 1.899ms (SLO: <2.100ms -9.6%) vs baseline: -0.1%

Memory: ✅ 49.628MB (SLO: <52.500MB -5.5%) vs baseline: +4.8%


✅ resource-renaming

Time: ✅ 3.404ms (SLO: <3.650ms -6.8%) vs baseline: +0.4%

Memory: ✅ 56.230MB (SLO: <60.000MB -6.3%) vs baseline: +4.4%


✅ tracer

Time: ✅ 3.417ms (SLO: <3.650ms -6.4%) vs baseline: ~same

Memory: ✅ 56.252MB (SLO: <60.000MB -6.2%) vs baseline: +4.5%


✅ tracer-native

Time: ✅ 3.413ms (SLO: <3.650ms -6.5%) vs baseline: ~same

Memory: ✅ 56.134MB (SLO: <60.000MB -6.4%) vs baseline: +4.5%


flasksqli - 6/6

✅ appsec-enabled

Time: ✅ 2.100ms (SLO: <4.200ms 📉 -50.0%) vs baseline: -0.5%

Memory: ✅ 56.525MB (SLO: <66.000MB 📉 -14.4%) vs baseline: +4.6%


✅ iast-enabled

Time: ✅ 2.112ms (SLO: <2.800ms 📉 -24.6%) vs baseline: +0.1%

Memory: ✅ 56.584MB (SLO: <62.500MB -9.5%) vs baseline: +4.6%


✅ tracer-enabled

Time: ✅ 2.098ms (SLO: <2.250ms -6.7%) vs baseline: -0.3%

Memory: ✅ 56.603MB (SLO: <60.000MB -5.7%) vs baseline: +4.7%


httppropagationinject - 16/16

✅ ids_only

Time: ✅ 20.414µs (SLO: <30.000µs 📉 -32.0%) vs baseline: -2.1%

Memory: ✅ 35.586MB (SLO: <38.000MB -6.4%) vs baseline: +4.3%


✅ with_all

Time: ✅ 26.259µs (SLO: <40.000µs 📉 -34.4%) vs baseline: -5.5%

Memory: ✅ 35.625MB (SLO: <38.000MB -6.2%) vs baseline: +4.3%


✅ with_dd_origin

Time: ✅ 23.281µs (SLO: <30.000µs 📉 -22.4%) vs baseline: -5.1%

Memory: ✅ 35.547MB (SLO: <38.000MB -6.5%) vs baseline: +3.9%


✅ with_priority_and_origin

Time: ✅ 22.695µs (SLO: <40.000µs 📉 -43.3%) vs baseline: -5.3%

Memory: ✅ 35.625MB (SLO: <38.000MB -6.2%) vs baseline: +4.3%


✅ with_sampling_priority

Time: ✅ 19.484µs (SLO: <30.000µs 📉 -35.1%) vs baseline: -6.2%

Memory: ✅ 35.586MB (SLO: <38.000MB -6.4%) vs baseline: +4.2%


✅ with_tags

Time: ✅ 24.328µs (SLO: <40.000µs 📉 -39.2%) vs baseline: -6.0%

Memory: ✅ 35.606MB (SLO: <38.000MB -6.3%) vs baseline: +4.2%


✅ with_tags_invalid

Time: ✅ 25.639µs (SLO: <40.000µs 📉 -35.9%) vs baseline: -6.0%

Memory: ✅ 35.625MB (SLO: <38.000MB -6.2%) vs baseline: +4.4%


✅ with_tags_max_size

Time: ✅ 24.749µs (SLO: <40.000µs 📉 -38.1%) vs baseline: -5.7%

Memory: ✅ 35.625MB (SLO: <38.000MB -6.2%) vs baseline: +4.5%


iastaspectssplit - 12/12

✅ rsplit_aspect

Time: ✅ 153.761µs (SLO: <250.000µs 📉 -38.5%) vs baseline: +2.2%

Memory: ✅ 42.625MB (SLO: <46.000MB -7.3%) vs baseline: +4.6%


✅ rsplit_noaspect

Time: ✅ 156.995µs (SLO: <250.000µs 📉 -37.2%) vs baseline: +1.7%

Memory: ✅ 42.585MB (SLO: <46.000MB -7.4%) vs baseline: +4.1%


✅ split_aspect

Time: ✅ 148.809µs (SLO: <250.000µs 📉 -40.5%) vs baseline: +0.5%

Memory: ✅ 42.585MB (SLO: <46.000MB -7.4%) vs baseline: +4.1%


✅ split_noaspect

Time: ✅ 154.937µs (SLO: <250.000µs 📉 -38.0%) vs baseline: +1.4%

Memory: ✅ 42.507MB (SLO: <46.000MB -7.6%) vs baseline: +4.2%


✅ splitlines_aspect

Time: ✅ 143.907µs (SLO: <250.000µs 📉 -42.4%) vs baseline: -0.2%

Memory: ✅ 42.467MB (SLO: <46.000MB -7.7%) vs baseline: +4.1%


✅ splitlines_noaspect

Time: ✅ 148.573µs (SLO: <250.000µs 📉 -40.6%) vs baseline: -0.7%

Memory: ✅ 42.467MB (SLO: <46.000MB -7.7%) vs baseline: +3.6%


iastpropagation - 8/8

✅ no-propagation

Time: ✅ 47.974µs (SLO: <60.000µs 📉 -20.0%) vs baseline: -0.3%

Memory: ✅ 39.007MB (SLO: <42.000MB -7.1%) vs baseline: +4.4%


✅ propagation_enabled

Time: ✅ 134.807µs (SLO: <190.000µs 📉 -29.0%) vs baseline: -0.2%

Memory: ✅ 39.027MB (SLO: <42.000MB -7.1%) vs baseline: +4.7%


✅ propagation_enabled_100

Time: ✅ 1.567ms (SLO: <2.300ms 📉 -31.9%) vs baseline: +0.3%

Memory: ✅ 39.007MB (SLO: <42.000MB -7.1%) vs baseline: +4.6%


✅ propagation_enabled_1000

Time: ✅ 29.147ms (SLO: <34.550ms 📉 -15.6%) vs baseline: +1.2%

Memory: ✅ 39.086MB (SLO: <42.000MB -6.9%) vs baseline: +4.6%


otelsdkspan - 24/24

✅ add-event

Time: ✅ 40.199ms (SLO: <42.000ms -4.3%) vs baseline: -0.7%

Memory: ✅ 37.926MB (SLO: <40.750MB -6.9%) vs baseline: +4.3%


✅ add-link

Time: ✅ 36.222ms (SLO: <38.550ms -6.0%) vs baseline: -0.2%

Memory: ✅ 38.221MB (SLO: <40.750MB -6.2%) vs baseline: +5.2%


✅ add-metrics

Time: ✅ 219.014ms (SLO: <232.000ms -5.6%) vs baseline: -0.5%

Memory: ✅ 37.985MB (SLO: <40.750MB -6.8%) vs baseline: +4.2%


✅ add-tags

Time: ✅ 211.148ms (SLO: <221.600ms -4.7%) vs baseline: -0.5%

Memory: ✅ 37.926MB (SLO: <40.750MB -6.9%) vs baseline: +4.1%


✅ get-context

Time: ✅ 29.274ms (SLO: <31.300ms -6.5%) vs baseline: -0.3%

Memory: ✅ 37.965MB (SLO: <40.750MB -6.8%) vs baseline: +4.3%


✅ is-recording

Time: ✅ 29.366ms (SLO: <31.000ms -5.3%) vs baseline: -0.1%

Memory: ✅ 37.926MB (SLO: <40.750MB -6.9%) vs baseline: +4.1%


✅ record-exception

Time: ✅ 62.771ms (SLO: <65.850ms -4.7%) vs baseline: -0.9%

Memory: ✅ 38.004MB (SLO: <40.750MB -6.7%) vs baseline: +4.5%


✅ set-status

Time: ✅ 32.045ms (SLO: <34.150ms -6.2%) vs baseline: -0.3%

Memory: ✅ 37.926MB (SLO: <40.750MB -6.9%) vs baseline: +4.0%


✅ start

Time: ✅ 29.142ms (SLO: <30.150ms -3.3%) vs baseline: +0.6%

Memory: ✅ 37.926MB (SLO: <40.750MB -6.9%) vs baseline: +4.3%


✅ start-finish

Time: ✅ 33.954ms (SLO: <35.350ms -3.9%) vs baseline: -0.3%

Memory: ✅ 38.122MB (SLO: <40.750MB -6.4%) vs baseline: +4.6%


✅ start-finish-telemetry

Time: ✅ 33.841ms (SLO: <35.450ms -4.5%) vs baseline: -1.3%

Memory: ✅ 37.985MB (SLO: <40.750MB -6.8%) vs baseline: +4.4%


✅ update-name

Time: ✅ 31.051ms (SLO: <33.400ms -7.0%) vs baseline: ~same

Memory: ✅ 37.945MB (SLO: <40.750MB -6.9%) vs baseline: +4.3%


otelspan - 22/22

✅ add-event

Time: ✅ 41.186ms (SLO: <47.150ms 📉 -12.6%) vs baseline: -1.6%

Memory: ✅ 40.497MB (SLO: <47.000MB 📉 -13.8%) vs baseline: +4.5%


✅ add-metrics

Time: ✅ 250.400ms (SLO: <344.800ms 📉 -27.4%) vs baseline: -1.0%

Memory: ✅ 44.872MB (SLO: <47.500MB -5.5%) vs baseline: +4.8%


✅ add-tags

Time: ✅ 307.136ms (SLO: <330.000ms -6.9%) vs baseline: -0.1%

Memory: ✅ 44.809MB (SLO: <47.500MB -5.7%) vs baseline: +4.6%


✅ get-context

Time: ✅ 80.934ms (SLO: <92.350ms 📉 -12.4%) vs baseline: -0.6%

Memory: ✅ 40.914MB (SLO: <46.500MB 📉 -12.0%) vs baseline: +5.0%


✅ is-recording

Time: ✅ 37.665ms (SLO: <44.500ms 📉 -15.4%) vs baseline: -1.3%

Memory: ✅ 40.277MB (SLO: <47.500MB 📉 -15.2%) vs baseline: +4.5%


✅ record-exception

Time: ✅ 59.966ms (SLO: <67.650ms 📉 -11.4%) vs baseline: -1.4%

Memory: ✅ 40.909MB (SLO: <47.000MB 📉 -13.0%) vs baseline: +4.6%


✅ set-status

Time: ✅ 44.017ms (SLO: <50.400ms 📉 -12.7%) vs baseline: -1.5%

Memory: ✅ 40.307MB (SLO: <47.000MB 📉 -14.2%) vs baseline: +4.6%


✅ start

Time: ✅ 38.407ms (SLO: <43.450ms 📉 -11.6%) vs baseline: +2.9%

Memory: ✅ 40.258MB (SLO: <47.000MB 📉 -14.3%) vs baseline: +4.3%


✅ start-finish

Time: ✅ 85.387ms (SLO: <90.000ms -5.1%) vs baseline: -1.2%

Memory: ✅ 38.122MB (SLO: <46.500MB 📉 -18.0%) vs baseline: +4.6%


✅ start-finish-telemetry

Time: ✅ 86.793ms (SLO: <91.000ms -4.6%) vs baseline: -1.2%

Memory: ✅ 38.044MB (SLO: <46.500MB 📉 -18.2%) vs baseline: +4.4%


✅ update-name

Time: ✅ 38.462ms (SLO: <45.150ms 📉 -14.8%) vs baseline: -2.0%

Memory: ✅ 40.462MB (SLO: <47.000MB 📉 -13.9%) vs baseline: +4.5%


packagespackageforrootmodulemapping - 4/4

✅ cache_off

Time: ✅ 345.091ms (SLO: <354.300ms -2.6%) vs baseline: -1.3%

Memory: ✅ 40.894MB (SLO: <46.000MB 📉 -11.1%) vs baseline: +3.4%


✅ cache_on

Time: ✅ 0.380µs (SLO: <10.000µs 📉 -96.2%) vs baseline: -0.9%

Memory: ✅ 40.344MB (SLO: <46.000MB 📉 -12.3%) vs baseline: +5.4%


rand - 2/2

✅ rand128bits

Time: ✅ 0.193µs (SLO: <21.000µs 📉 -99.1%) vs baseline: +4.6%


✅ rand64bits

Time: ✅ 0.126µs (SLO: <15.000µs 📉 -99.2%) vs baseline: +7.4%


ratelimiter - 12/12

✅ defaults

Time: ✅ 2.341µs (SLO: <10.000µs 📉 -76.6%) vs baseline: -1.7%

Memory: ✅ 35.507MB (SLO: <38.000MB -6.6%) vs baseline: +4.2%


✅ high_rate_limit

Time: ✅ 2.412µs (SLO: <10.000µs 📉 -75.9%) vs baseline: -2.8%

Memory: ✅ 35.566MB (SLO: <38.000MB -6.4%) vs baseline: +4.5%


✅ long_window

Time: ✅ 2.339µs (SLO: <10.000µs 📉 -76.6%) vs baseline: -1.9%

Memory: ✅ 35.586MB (SLO: <38.000MB -6.4%) vs baseline: +4.7%


✅ low_rate_limit

Time: ✅ 2.362µs (SLO: <10.000µs 📉 -76.4%) vs baseline: -1.0%

Memory: ✅ 35.606MB (SLO: <38.000MB -6.3%) vs baseline: +4.5%


✅ no_rate_limit

Time: ✅ 0.823µs (SLO: <10.000µs 📉 -91.8%) vs baseline: -2.3%

Memory: ✅ 35.527MB (SLO: <38.000MB -6.5%) vs baseline: +4.5%


✅ short_window

Time: ✅ 2.472µs (SLO: <10.000µs 📉 -75.3%) vs baseline: -2.2%

Memory: ✅ 35.606MB (SLO: <38.000MB -6.3%) vs baseline: +4.6%


recursivecomputation - 8/8

✅ deep

Time: ✅ 310.279ms (SLO: <320.950ms -3.3%) vs baseline: ~same

Memory: ✅ 36.333MB (SLO: <38.750MB -6.2%) vs baseline: +4.3%


✅ deep-profiled

Time: ✅ 330.093ms (SLO: <359.150ms -8.1%) vs baseline: +0.2%

Memory: ✅ 40.501MB (SLO: <46.000MB 📉 -12.0%) vs baseline: +4.4%


✅ medium

Time: ✅ 7.169ms (SLO: <7.400ms -3.1%) vs baseline: +0.6%

Memory: ✅ 35.547MB (SLO: <38.000MB -6.5%) vs baseline: +4.3%


✅ shallow

Time: ✅ 0.994ms (SLO: <1.050ms -5.3%) vs baseline: +1.9%

Memory: ✅ 35.547MB (SLO: <38.000MB -6.5%) vs baseline: +4.6%


samplingrules - 8/8

✅ average_match

Time: ✅ 147.820µs (SLO: <290.000µs 📉 -49.0%) vs baseline: -1.0%

Memory: ✅ 35.625MB (SLO: <38.000MB -6.2%) vs baseline: +4.5%


✅ high_match

Time: ✅ 193.743µs (SLO: <480.000µs 📉 -59.6%) vs baseline: -1.7%

Memory: ✅ 35.724MB (SLO: <38.000MB -6.0%) vs baseline: +4.5%


✅ low_match

Time: ✅ 98.767µs (SLO: <120.000µs 📉 -17.7%) vs baseline: -0.8%

Memory: ✅ 707.106MB (SLO: <780.000MB -9.3%) vs baseline: +4.8%


✅ very_low_match

Time: ✅ 2.857ms (SLO: <8.500ms 📉 -66.4%) vs baseline: -3.0%

Memory: ✅ 78.307MB (SLO: <85.000MB -7.9%) vs baseline: +4.5%


sethttpmeta - 32/32

✅ all-disabled

Time: ✅ 10.557µs (SLO: <20.000µs 📉 -47.2%) vs baseline: +0.4%

Memory: ✅ 35.920MB (SLO: <38.750MB -7.3%) vs baseline: +3.7%


✅ all-enabled

Time: ✅ 41.055µs (SLO: <50.000µs 📉 -17.9%) vs baseline: +2.3%

Memory: ✅ 36.019MB (SLO: <38.750MB -7.0%) vs baseline: +4.1%


✅ collectipvariant_exists

Time: ✅ 40.905µs (SLO: <50.000µs 📉 -18.2%) vs baseline: +0.6%

Memory: ✅ 36.058MB (SLO: <38.750MB -6.9%) vs baseline: +4.6%


✅ no-collectipvariant

Time: ✅ 40.013µs (SLO: <50.000µs 📉 -20.0%) vs baseline: -0.5%

Memory: ✅ 36.097MB (SLO: <38.750MB -6.8%) vs baseline: +4.0%


✅ no-useragentvariant

Time: ✅ 38.793µs (SLO: <50.000µs 📉 -22.4%) vs baseline: ~same

Memory: ✅ 35.940MB (SLO: <38.750MB -7.3%) vs baseline: +4.1%


✅ obfuscation-no-query

Time: ✅ 40.957µs (SLO: <50.000µs 📉 -18.1%) vs baseline: +1.4%

Memory: ✅ 36.117MB (SLO: <38.750MB -6.8%) vs baseline: +4.5%


✅ obfuscation-regular-case-explicit-query

Time: ✅ 76.424µs (SLO: <90.000µs 📉 -15.1%) vs baseline: +0.3%

Memory: ✅ 36.333MB (SLO: <38.750MB -6.2%) vs baseline: +4.3%


✅ obfuscation-regular-case-implicit-query

Time: ✅ 76.926µs (SLO: <90.000µs 📉 -14.5%) vs baseline: +0.3%

Memory: ✅ 36.353MB (SLO: <38.750MB -6.2%) vs baseline: +4.1%


✅ obfuscation-send-querystring-disabled

Time: ✅ 154.984µs (SLO: <170.000µs -8.8%) vs baseline: -0.4%

Memory: ✅ 36.372MB (SLO: <38.750MB -6.1%) vs baseline: +4.2%


✅ obfuscation-worst-case-explicit-query

Time: ✅ 149.484µs (SLO: <160.000µs -6.6%) vs baseline: ~same

Memory: ✅ 36.372MB (SLO: <38.750MB -6.1%) vs baseline: +4.2%


✅ obfuscation-worst-case-implicit-query

Time: ✅ 155.622µs (SLO: <170.000µs -8.5%) vs baseline: -0.2%

Memory: ✅ 36.412MB (SLO: <38.750MB -6.0%) vs baseline: +4.3%


✅ useragentvariant_exists_1

Time: ✅ 39.502µs (SLO: <50.000µs 📉 -21.0%) vs baseline: ~same

Memory: ✅ 35.940MB (SLO: <38.750MB -7.3%) vs baseline: +3.9%


✅ useragentvariant_exists_2

Time: ✅ 40.723µs (SLO: <50.000µs 📉 -18.6%) vs baseline: +0.5%

Memory: ✅ 35.999MB (SLO: <38.750MB -7.1%) vs baseline: +3.9%


✅ useragentvariant_exists_3

Time: ✅ 40.163µs (SLO: <50.000µs 📉 -19.7%) vs baseline: +0.6%

Memory: ✅ 36.097MB (SLO: <38.750MB -6.8%) vs baseline: +4.0%


✅ useragentvariant_not_exists_1

Time: ✅ 39.630µs (SLO: <50.000µs 📉 -20.7%) vs baseline: +0.7%

Memory: ✅ 36.176MB (SLO: <38.750MB -6.6%) vs baseline: +4.4%


✅ useragentvariant_not_exists_2

Time: ✅ 39.481µs (SLO: <50.000µs 📉 -21.0%) vs baseline: ~same

Memory: ✅ 36.137MB (SLO: <38.750MB -6.7%) vs baseline: +4.3%


span - 26/26

✅ add-event

Time: ✅ 18.838ms (SLO: <22.500ms 📉 -16.3%) vs baseline: -3.5%

Memory: ✅ 37.631MB (SLO: <53.000MB 📉 -29.0%) vs baseline: +4.0%


✅ add-metrics

Time: ✅ 88.683ms (SLO: <93.500ms -5.2%) vs baseline: -0.4%

Memory: ✅ 42.212MB (SLO: <53.000MB 📉 -20.4%) vs baseline: +5.1%


✅ add-tags

Time: ✅ 144.983ms (SLO: <155.000ms -6.5%) vs baseline: -0.6%

Memory: ✅ 42.056MB (SLO: <53.000MB 📉 -20.6%) vs baseline: +4.3%


✅ get-context

Time: ✅ 17.213ms (SLO: <20.500ms 📉 -16.0%) vs baseline: -2.9%

Memory: ✅ 37.493MB (SLO: <53.000MB 📉 -29.3%) vs baseline: +4.3%


✅ is-recording

Time: ✅ 17.219ms (SLO: <20.500ms 📉 -16.0%) vs baseline: -3.0%

Memory: ✅ 37.473MB (SLO: <53.000MB 📉 -29.3%) vs baseline: +4.0%


✅ record-exception

Time: ✅ 38.179ms (SLO: <41.000ms -6.9%) vs baseline: -2.0%

Memory: ✅ 38.163MB (SLO: <53.000MB 📉 -28.0%) vs baseline: +4.5%


✅ set-status

Time: ✅ 19.230ms (SLO: <22.000ms 📉 -12.6%) vs baseline: -2.2%

Memory: ✅ 37.415MB (SLO: <53.000MB 📉 -29.4%) vs baseline: +4.0%


✅ start

Time: ✅ 18.220ms (SLO: <20.500ms 📉 -11.1%) vs baseline: +3.7%

Memory: ✅ 37.552MB (SLO: <53.000MB 📉 -29.1%) vs baseline: +4.3%


✅ start-finish

Time: ✅ 53.680ms (SLO: <56.000ms -4.1%) vs baseline: -1.2%

Memory: ✅ 35.547MB (SLO: <38.000MB -6.5%) vs baseline: +4.3%


✅ start-finish-telemetry

Time: ✅ 54.743ms (SLO: <58.000ms -5.6%) vs baseline: -1.5%

Memory: ✅ 35.586MB (SLO: <38.000MB -6.4%) vs baseline: +4.3%


✅ start-finish-traceid128

Time: ✅ 56.372ms (SLO: <60.000ms -6.0%) vs baseline: -1.5%

Memory: ✅ 35.665MB (SLO: <38.000MB -6.1%) vs baseline: +4.2%


✅ start-traceid128

Time: ✅ 17.176ms (SLO: <22.500ms 📉 -23.7%) vs baseline: -3.2%

Memory: ✅ 37.552MB (SLO: <53.000MB 📉 -29.1%) vs baseline: +4.7%


✅ update-name

Time: ✅ 17.718ms (SLO: <22.000ms 📉 -19.5%) vs baseline: -2.8%

Memory: ✅ 37.591MB (SLO: <53.000MB 📉 -29.1%) vs baseline: +4.4%

ℹ️ Scenarios Missing SLO Configuration (46 scenarios)

The following scenarios exist in candidate data but have no SLO thresholds configured:

  • coreapiscenario-core_dispatch_listeners
  • coreapiscenario-core_dispatch_no_listeners
  • coreapiscenario-core_dispatch_with_results_listeners
  • coreapiscenario-core_dispatch_with_results_no_listeners
  • djangosimple-baseline
  • errortrackingdjangosimple-baseline
  • errortrackingflasksqli-baseline
  • flasksimple-baseline
  • flasksqli-baseline
  • iast_aspects-re_expand_aspect
  • iast_aspects-re_expand_noaspect
  • iast_aspects-re_findall_aspect
  • iast_aspects-re_findall_noaspect
  • iast_aspects-re_finditer_aspect
  • iast_aspects-re_finditer_noaspect
  • iast_aspects-re_fullmatch_aspect
  • iast_aspects-re_fullmatch_noaspect
  • iast_aspects-re_group_aspect
  • iast_aspects-re_group_noaspect
  • iast_aspects-re_groups_aspect
  • iast_aspects-re_groups_noaspect
  • iast_aspects-re_match_aspect
  • iast_aspects-re_match_noaspect
  • iast_aspects-re_search_aspect
  • iast_aspects-re_search_noaspect
  • iast_aspects-re_sub_aspect
  • iast_aspects-re_sub_noaspect
  • iast_aspects-re_subn_aspect
  • iast_aspects-re_subn_noaspect
  • sethttpmeta-obfuscation-disabled
  • startup-baseline
  • startup-baseline_django
  • startup-baseline_flask
  • startup-ddtrace_run
  • startup-ddtrace_run_appsec
  • startup-ddtrace_run_profiling
  • startup-ddtrace_run_runtime_metrics
  • startup-ddtrace_run_send_span
  • startup-ddtrace_run_telemetry_disabled
  • startup-ddtrace_run_telemetry_enabled
  • startup-import_ddtrace
  • startup-import_ddtrace_auto
  • startup-import_ddtrace_auto_django
  • startup-import_ddtrace_auto_flask
  • startup-import_ddtrace_django
  • startup-import_ddtrace_flask

@P403n1x87
P403n1x87 force-pushed the refactor/native-forksafe-threads branch from 8f151dd to ced3c89 Compare July 29, 2025 11:50
Comment thread ddtrace/internal/_threads.cpp Outdated
@P403n1x87
P403n1x87 force-pushed the refactor/native-forksafe-threads branch 3 times, most recently from d00259c to 9967a4d Compare July 30, 2025 10:57
Comment thread ddtrace/profiling/profiler.py
@P403n1x87
P403n1x87 marked this pull request as ready for review July 31, 2025 14:26
@P403n1x87
P403n1x87 requested review from a team as code owners July 31, 2025 14:26
@taegyunkim
taegyunkim self-requested a review August 4, 2025 20:05
@emmettbutler
emmettbutler marked this pull request as draft August 7, 2025 16:41
@nsrip-dd

Copy link
Copy Markdown
Contributor

One thing that came up in our recent discussion I want to re-state here: I think we need to make sure that starting and stopping the periodic threads doesn't indefinitely delay the periodic callbacks. Specifically, thinking of something like profiling, where we upload data every 60 seconds. If we exit the thread, then the 60 second timer is restarted when the thread is restarted. And so if a program forks more than once every 60 seconds we will never actually upload a profile from the parent process. I think if we stick to this approach, we probably want a deadline for the periodic thread. And the time the thread sleeps is relative to the time left until the deadline. The deadline wouldn't reset by default when stopping/restarting the thread, only when the callback is called. LMK if that makes sense, I'm happy to sketch it out more.

@P403n1x87
P403n1x87 marked this pull request as ready for review August 13, 2025 09:45
@P403n1x87
P403n1x87 requested a review from a team as a code owner August 13, 2025 09:45
@P403n1x87

P403n1x87 commented Aug 13, 2025

Copy link
Copy Markdown
Contributor Author

we need to make sure that starting and stopping the periodic threads doesn't indefinitely delay the periodic callbacks.

does 817730d8796cf406651590daf9a2ffd94050bbf9
work?

We refactor the native periodict thread implementation to be
fork-safe, the sense that all such threads that are running at the
time of a fork are automatically stopped before the fork, then
restarted after it. We also take care to avoid stopping and
restarting threads in the parent process if we detect an immediate
call to fork again.
@juanjux

juanjux commented Feb 6, 2026

Copy link
Copy Markdown
Collaborator

I added some forktime benchmarks results here (linking a gist as not to take over the length of this PR page):
gist.github.com/juanjux/79ee7b82c26dd2ff664a8d024ebeabb4_ (too large to embed)_
If the benchmarking is right, looks like there is some overhead, which seem to also increase over iterations.

Thanks, I think I have spotted an issue that could explain the accumulation issue. Pushing a commit to fix that now

That was fast! From my tests, the accumulation is fixed, and the average overhead lowered to only 13%

https://gist.github.com/juanjux/cd17a8765d1dd9cf171852ca5069f93a

@P403n1x87

P403n1x87 commented Feb 6, 2026

Copy link
Copy Markdown
Contributor Author

Updated Performance Analysis

The same benchmark scenario of 1000 forks in a tight loop yields the following results for the average fork execution time

Baseline PR
0.3 ms 1.8 ms

The wall time profile is in line with the original analysis

Screenshot 2026-02-06 at 15 14 56

Native profiles show that most of the time is spent joining the threads in between forks during the execution of the before-fork hook

Screenshot 2026-02-06 at 15 15 45

Comment thread ddtrace/internal/threads.py
@DataDog DataDog deleted a comment from github-actions Bot Feb 10, 2026

@taegyunkim taegyunkim left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry that I'm late! A few minor points.

Comment thread ddtrace/internal/threads.py Outdated
Comment thread ddtrace/internal/_threads.cpp Outdated
@taegyunkim

Copy link
Copy Markdown
Contributor

Haven't had a chance to look at it yet, but we got a coredump from https://gitlab.ddbuild.io/DataDog/apm-reliability/dd-trace-py/-/jobs/1412900511 this debugger suite

@P403n1x87

Copy link
Copy Markdown
Contributor Author

Haven't had a chance to look at it yet, but we got a coredump from https://gitlab.ddbuild.io/DataDog/apm-reliability/dd-trace-py/-/jobs/1412900511 this debugger suite

That seems to be unrelated to this change, but let's save the dump so that we can investigate

@P403n1x87
P403n1x87 force-pushed the refactor/native-forksafe-threads branch from 6aa6235 to 8501928 Compare February 13, 2026 11:56
@P403n1x87
P403n1x87 force-pushed the refactor/native-forksafe-threads branch from 8501928 to 881c153 Compare February 13, 2026 11:57
@brettlangdon
brettlangdon merged commit 14ebe21 into main Feb 13, 2026
1042 of 1045 checks passed
@brettlangdon
brettlangdon deleted the refactor/native-forksafe-threads branch February 13, 2026 14:05
gh-worker-dd-mergequeue-cf854d Bot pushed a commit that referenced this pull request Feb 18, 2026
…er fork() (#16545)

## Description

After #14163, we started to see a profiler behavior change in `dd-trace-doe` CI:
- `TestLanguage/python/flush/false` expected `0` profiles, got `3`. https://github.com/DataDog/dd-trace-doe/actions/runs/22102391228/job/63875669025

- During pre-fork, `PeriodicThread.stop()` sets the internal request event to wake the periodic threads promptly. 
- After fork restart, that same request could be interpreted as a real wakeup, causing an immediate `periodic()` run. 
- For profiling, that means an immediate `upload()` right after fork, which results in unexpected profile exports.

What this PR changfes:
- Distinguishes a fork-stop wakeup from a real `awake()` request in `PeriodicThread`
- Clears the request after fork only when it came from the pre-fork stop path.
- Preserves real `awake()` behavior across restart windows.


## Testing

<!-- Describe your testing strategy or note what tests are included -->

## Risks

<!-- Note any risks associated with this change, or "None" if no risks -->

## Additional Notes

<!-- Any other information that would be helpful for reviewers -->


Co-authored-by: taegyun.kim <[email protected]>
r1viollet added a commit that referenced this pull request Mar 3, 2026
During Python finalization, CPython's take_gil calls pthread_exit() on
non-main threads attempting to reacquire the GIL. On glibc, pthread_exit
is implemented via abi::__forced_unwind — a forced stack unwind that, if
it escapes a std::thread callable without proper handling, triggers
std::terminate and a SIGABRT.

This was made reachable by the native fork-safe threads refactor
(PR #14163) which actively restarts periodic threads in forked child
processes. When such a child exits, threads may be mid-callback when
finalization begins, and CPython kills them via pthread_exit before the
atexit handler's stop signal can be processed.

Changes:
- Catch abi::__forced_unwind in the thread lambda (glibc only), signal
  _stopped so join() unblocks, and re-throw to let the unwind complete
  through libstdc++'s std::thread wrapper.
- Make _atexit() tolerate _thread == nullptr instead of raising.
- Check start() return value in _after_fork() to avoid silent failures.
- Add try/except in the Python atexit handler so one thread's failure
  does not skip stopping the rest.
KowalskiThomas pushed a commit that referenced this pull request Mar 13, 2026
…er fork() (#16545)

## Description

After #14163, we started to see a profiler behavior change in `dd-trace-doe` CI:
- `TestLanguage/python/flush/false` expected `0` profiles, got `3`. https://github.com/DataDog/dd-trace-doe/actions/runs/22102391228/job/63875669025

- During pre-fork, `PeriodicThread.stop()` sets the internal request event to wake the periodic threads promptly.
- After fork restart, that same request could be interpreted as a real wakeup, causing an immediate `periodic()` run.
- For profiling, that means an immediate `upload()` right after fork, which results in unexpected profile exports.

What this PR changfes:
- Distinguishes a fork-stop wakeup from a real `awake()` request in `PeriodicThread`
- Clears the request after fork only when it came from the pre-fork stop path.
- Preserves real `awake()` behavior across restart windows.

## Testing

<!-- Describe your testing strategy or note what tests are included -->

## Risks

<!-- Note any risks associated with this change, or "None" if no risks -->

## Additional Notes

<!-- Any other information that would be helpful for reviewers -->

Co-authored-by: taegyun.kim <[email protected]>
(cherry picked from commit 295a47f)
KowalskiThomas pushed a commit that referenced this pull request Mar 13, 2026
…er fork()

After #14163, we started to see a profiler behavior change in `dd-trace-doe` CI:
- `TestLanguage/python/flush/false` expected `0` profiles, got `3`. https://github.com/DataDog/dd-trace-doe/actions/runs/22102391228/job/63875669025

- During pre-fork, `PeriodicThread.stop()` sets the internal request event to wake the periodic threads promptly.
- After fork restart, that same request could be interpreted as a real wakeup, causing an immediate `periodic()` run.
- For profiling, that means an immediate `upload()` right after fork, which results in unexpected profile exports.

What this PR changfes:
- Distinguishes a fork-stop wakeup from a real `awake()` request in `PeriodicThread`
- Clears the request after fork only when it came from the pre-fork stop path.
- Preserves real `awake()` behavior across restart windows.

<!-- Describe your testing strategy or note what tests are included -->

<!-- Note any risks associated with this change, or "None" if no risks -->

<!-- Any other information that would be helpful for reviewers -->

Co-authored-by: taegyun.kim <[email protected]>
(cherry picked from commit 295a47f)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog/no-changelog A changelog entry is not required for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.