You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Urllib3Instrumentation patches both PoolManager.urlopen() and HTTPConnectionPool.urlopen() to cover two distinct user-facing entry paths. However, when a request goes through PoolManager, it internally delegates to HTTPConnectionPool.urlopen(), causing both patches to fire and producing two identical spans for a single HTTP request (a parent PoolManager span and a redundant child ConnectionPool span).
This fix introduces a ContextVar (_inside_poolmanager) that the PoolManager patch sets to True for the duration of its call. The ConnectionPool patch checks this flag and passes through without creating a span when it detects the request already originated from PoolManager. Direct HTTPConnectionPool usage (bypassing PoolManager) is unaffected since the flag remains False.
Changes
Added _inside_poolmanagerContextVar to signal when a request is already being handled by the PoolManager patch
Set the flag in _patch_pool_manager and reset it in the finally block (thread-safe and async-safe)
Added early-exit check in _patch_connection_pool to skip span creation when inside a PoolManager call
Extracted repeated passthrough argument lists in the ConnectionPool patch into a functools.partial (_passthrough) to reduce duplication across the three early-return paths (disabled, inside-poolmanager, higher-level instrumentation)
Tusk's tests all pass and validate the core deduplication fix. The ContextVar (_inside_poolmanager) is properly initialized, set/reset during PoolManager execution, and isolated across contexts—critical for thread-safety. The PoolManager patch correctly sets the flag and cleans up even on exceptions, while the ConnectionPool patch skips span creation when the flag is set but still instruments direct ConnectionPool calls. The integration test confirms the fix works end-to-end: a single request through PoolManager now produces one span instead of two. This directly addresses the duplicate span problem that was polluting traces and making debugging harder.
Key Points:
_inside_poolmanager context isolation prevents state leaks across concurrent requests
PoolManager patch reliably sets/resets the flag, including exception paths
ConnectionPool patch correctly gates span creation based on the flag while preserving direct usage instrumentation
Integration test validates the deduplication works in practice—no more redundant child spans
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
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.
Summary
Urllib3Instrumentationpatches bothPoolManager.urlopen()andHTTPConnectionPool.urlopen()to cover two distinct user-facing entry paths. However, when a request goes throughPoolManager, it internally delegates toHTTPConnectionPool.urlopen(), causing both patches to fire and producing two identical spans for a single HTTP request (a parent PoolManager span and a redundant child ConnectionPool span).This fix introduces a
ContextVar(_inside_poolmanager) that the PoolManager patch sets toTruefor the duration of its call. The ConnectionPool patch checks this flag and passes through without creating a span when it detects the request already originated from PoolManager. DirectHTTPConnectionPoolusage (bypassing PoolManager) is unaffected since the flag remainsFalse.Changes
_inside_poolmanagerContextVarto signal when a request is already being handled by the PoolManager patch_patch_pool_managerand reset it in thefinallyblock (thread-safe and async-safe)_patch_connection_poolto skip span creation when inside a PoolManager callfunctools.partial(_passthrough) to reduce duplication across the three early-return paths (disabled, inside-poolmanager, higher-level instrumentation)