chore(tracing): avoid reentrant SpanData borrow panic - #19699
Conversation
Coerce Python span attributes before taking a mutable native borrow so lazy values can safely start nested spans.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d8e1f22327
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
This change is marked for backport to 4.14 and it does not conflict with that branch. |
BenchmarksBenchmark execution time: 2026-08-14 16:35:06 Comparing candidate commit 8905c27 in PR branch Found 0 performance improvements and 8 performance regressions! Performance is the same for 610 metrics, 10 unstable metrics.
|
brettlangdon
left a comment
There was a problem hiding this comment.
Some test coverage missing is _set_attributes re-entrancy, and should we get some tests for _set_default_attributes / set_default_attribute ?
Co-authored-by: Brett Langdon <brett.langdon@datadoghq.com>
Co-authored-by: Brett Langdon <brett.langdon@datadoghq.com>
Codeowners resolved asResolved from the full PR diff against |
Circular import analysis
|
Dependency direction analysis
|
@brettlangdon agreed, done. |
Description
Dogweb's dd-trace-py 4.14.0rc2 dogfooding exposed a re-entrant PyO3 borrow panic under CI Visibility, Flask, and SQLAlchemy workloads:
The failure starts when a non-primitive span tag is converted to a string. In the observed case, the object's
__str__accessed an expired SQLAlchemy attribute, which issued a query and started a nested database span. Starting that span callscontext_provider.active().SpanData._set_attributepreviously held a mutable PyO3 borrow for the entire method, including while calling user-defined__str__and__index__. The native context provider introduced in 4.14 reads the activeSpanDatawith a shared borrow. Re-entering it during string conversion therefore attempted a shared borrow while the mutable borrow was still live, causing PyO3 to panic. When this occurred during a SQLAlchemy transaction, the abrupt exception could also leave the session in thepreparedstate and cause later tests to fail.This change performs all potentially re-entrant Python coercion before borrowing
SpanDatamutably. The mutable borrow is now limited to updating the native attribute map, and replaced Python values are dropped only after that borrow is released. Bulk and default attribute setters follow the same pattern.Additional Notes
The SQLAlchemy
preparedsession errors are downstream fallout from the panic, not a separate SQLAlchemy integration issue. This fixes the invalid borrow at its source rather than catching or suppressingPanicExceptionin an integration.