open-telemetry / open-telemetry/opentelemetry-php

[contrib-auto-doctrine] Duplicate spans when Doctrine connection wrapper chain fires hooks at multiple layers

Open
#1,931 2 comments 1 reaction 0 assignees View on GitHub

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-doctrine 0.3.1
  • Doctrine DBAL with connection wrapper chain
  • PHP 8.1+

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.