open-telemetry / open-telemetry/opentelemetry-php
[contrib-auto-doctrine] Duplicate spans when Doctrine connection wrapper chain fires hooks at multiple layers
Nobody has claimed this yet.
- Dominant language
- PHP
- Stars
- 912
- Forks
- 232
- Avg merge
- 7d 16h
- Merged PRs (30d)
- 4
Description
Component
open-telemetry/opentelemetry-auto-doctrine (https://github.com/opentelemetry-php/contrib-auto-doctrine)
Problem
When using Doctrine with a connection wrapper chain (e.g. custom middleware or any Driver that delegates to another Driver), the auto-instrumentation hooks fire at every layer of the chain. This produces duplicate spans for a single logical database operation, making traces noisy and misleading.
Root cause
DoctrineInstrumentation registers hooks on \Doctrine\DBAL\Driver::connect, \Doctrine\DBAL\Driver\Connection::query, exec, prepare, beginTransaction, commit, rollBack, and \Doctrine\DBAL\Driver\Statement::execute. When a connection wrapper delegates to an inner driver/connection, each hooked method fires the pre/post callbacks once per layer in the chain.
Expected behaviour
One span per logical database operation, regardless of how many wrapper layers are in the chain.
Actual behaviour
N spans per operation where N = number of wrapper layers.
Proposed fix
Add a per-method re-entrancy depth counter as a static property. The pre-hook increments the counter and skips span creation if depth > 1. The post-hook decrements the counter and only calls end() at depth 0.
private static array $depth = [];
// pre-hook:
self::$depth['query'] = (self::$depth['query'] ?? 0) + 1;
if (self::$depth['query'] > 1) {
return null;
}
// post-hook:
self::$depth['query'] = max(0, (self::$depth['query'] ?? 1) - 1);
if (self::$depth['query'] === 0) {
self::end($exception);
}
This ensures only the outermost invocation creates and ends a span. A PR with the fix for all 8 hooked methods is forthcoming.
Environment
open-telemetry/opentelemetry-auto-doctrine0.3.1- Doctrine DBAL with connection wrapper chain
- PHP 8.1+
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in the contrib-auto-doctrine component and trace DoctrineInstrumentation's hooks for Driver::connect, connection operations, and Statement::execute. Compare the pre/post callbacks across wrapper layers; done means a logical operation produces one span while preserving correct exception handling and coverage for all eight hooked methods.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- php
- Domain
- observability
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100