fix(integrations): asyncio: continuation trace transactions#5526
fix(integrations): asyncio: continuation trace transactions#5526constantinius wants to merge 1 commit intomasterfrom
Conversation
Semver Impact of This PR🟢 Patch (bug fixes) 📋 Changelog PreviewThis is how your changes will appear in the changelog. Bug Fixes 🐛Openai
Other
Documentation 📚
Internal Changes 🔧Agents
Openai Agents
Other
🤖 This preview updates automatically when you update the PR. |
Codecov Results 📊✅ 13 passed | Total: 13 | Pass Rate: 100% | Execution Time: 8.16s All tests are passing successfully. ❌ Patch coverage is 0.00%. Project has 13735 uncovered lines. Files with missing lines (180)
Generated by Codecov Action |
|
|
||
| if task_spans and in_span: | ||
| transaction = sentry_sdk.continue_trace( | ||
| headers, op="task", name="downstream" |
There was a problem hiding this comment.
Hardcoded transaction name 'downstream' loses coroutine-specific tracing context
The new code uses a hardcoded name="downstream" instead of name=get_name(coro) which previously captured the actual coroutine function name (e.g., 'foo', 'bar'). This eliminates meaningful trace identification - all async tasks will now appear as 'downstream' rather than their actual function names, making debugging and performance analysis significantly harder.
Verification
Read the full asyncio.py file and compared the diff. The original code at line ~65 used name=get_name(coro) to capture the coroutine name. The new code at line 77 hardcodes name="downstream". Tests in test_asyncio.py (lines 94, 101) expect transaction_event["spans"][1]["description"] == "foo" which will fail with this change.
Suggested fix: Pass the coroutine name to continue_trace and start_transaction
| headers, op="task", name="downstream" | |
| coro_name = get_name(coro) | |
| headers, op=OP.FUNCTION, name=coro_name, origin=AsyncioIntegration.origin |
Identified by Warden find-bugs · MFT-ANR
| transaction = sentry_sdk.continue_trace( | ||
| headers, op="task", name="downstream" | ||
| ) |
There was a problem hiding this comment.
Origin attribute not set on new transactions, breaking trace origin tracking
The original code set origin=AsyncioIntegration.origin (which equals 'auto.function.asyncio') on spans. The new transaction creation doesn't pass an origin, so it will default to 'manual'. This breaks the test at test_asyncio.py:395 which expects event["spans"][0]["origin"] == "auto.function.asyncio" and loses the ability to identify spans created by the asyncio integration.
Verification
Compared the diff showing removal of origin=AsyncioIntegration.origin. Verified that continue_trace in scope.py doesn't propagate origin to child spans. Test at test_asyncio.py line 395 checks for 'auto.function.asyncio' origin.
Identified by Warden find-bugs · TZS-6DB
| ) | ||
| task_spans = integration.task_spans if integration else False | ||
|
|
||
| if task_spans and in_span: |
There was a problem hiding this comment.
Changed conditional skips span creation when task_spans=True but no active span exists
The condition changed from if task_spans to if task_spans and in_span. This means when task_spans=True but there's no current active span (in_span=False), no transaction/span will be created at all. Previously, a span would still be created in this case. This is a behavioral change that could result in missing traces for orphaned async tasks.
Verification
Compared the original condition if task_spans (line ~64 in context) with new condition if task_spans and in_span at line 75. When in_span is False but task_spans is True, the new code enters the else branch and uses nullcontext(), creating nothing.
Identified by Warden find-bugs · DA3-SV4
| **kwargs: "Any", | ||
| ) -> "asyncio.Future[Any]": | ||
| # we get the current span here, as later it may already be closed | ||
| in_span = sentry_sdk.get_current_span() is not None |
There was a problem hiding this comment.
We have to get the info whether we are in a transaction straight away before it closes.
| ) | ||
| task_spans = integration.task_spans if integration else False | ||
|
|
||
| if task_spans and in_span: |
There was a problem hiding this comment.
We should probably add a new option if we want to go that route
|
Another way would be to have a function that wraps def create_continuation_task(coro, *args, **kwargs):
async def _wrapper():
headers = dict(
sentry_sdk.get_current_scope().iter_trace_propagation_headers()
)
transaction = sentry_sdk.continue_trace(
headers, op="task", name="downstream"
)
with sentry_sdk.start_transaction(transaction):
await coro
asyncio.create_task(_wrapper(), *args, **kwargs) |
Description
Starting a task within a transaction may result in dropped spans because the task could outlive the transaction.
This PR introduces opening transactions instead with continuation headers applied, so that the trace is continued.
Issues