Claus Ibsen created CAMEL-23708:
-----------------------------------
Summary: OpenTelemetry2 - Missing RECEIVED spans for routes
consuming from stub/seda in nested async paths
Key: CAMEL-23708
URL: https://issues.apache.org/jira/browse/CAMEL-23708
Project: Camel
Issue Type: Bug
Components: camel-opentelemetry
Reporter: Claus Ibsen
When using the stub component to simulate Kafka (e.g. with {{--observe}} / dev
profile), route consumer RECEIVED spans are not exported for routes that
consume from stub endpoints when the producer side is inside a multicast on a
seda consumer thread.
h3. Steps to reproduce
Run the route-topology example with OpenTelemetry dev mode:
{code}
camel run route-topology.camel.yaml --observe
{code}
The example has this flow:
{code}
timer -> order-generator -> direct:process-order -> process-order ->
kafka:orders
-> order-dispatcher (multicast)
-> kafka:fulfillment -> fulfillment route -> kafka:warehouse-shipments
-> kafka:notifications -> notification route -> kafka:email-outbox
{code}
h3. Expected behavior
Each trace should have 27 spans including EVENT_RECEIVED spans for all 6 routes:
- timer (order-generator) - OK
- direct:process-order (process-order) - OK
- direct:validate-order (validate-order) - OK
- kafka:orders via stub (order-dispatcher) - OK
- kafka:fulfillment via stub (fulfillment) - MISSING
- kafka:notifications via stub (notification) - MISSING
h3. Actual behavior
Each trace has exactly 25 spans. The EVENT_RECEIVED spans for the fulfillment
and notification routes are never exported to the DevSpanExporter.
The RECEIVED spans ARE created (processor spans log4, to7, log5, to8 correctly
reference them as parentSpanId), but {{span.end()}} is apparently never called,
so {{SimpleSpanProcessor.onEnd()}} never fires and they never reach the
exporter.
h3. Analysis
The issue is specific to nested async routing: the fulfillment and notification
routes consume from stub endpoints whose producers are inside a multicast
within the order-dispatcher route, which is itself on a seda consumer thread.
The {{onExchangeDone}} route policy callback ({{TracingRoutePolicy}} in
{{camel-telemetry}}) does not fire for these exchanges, so the RECEIVED span is
started but never ended/exported.
The kafka:orders RECEIVED span (order-dispatcher route) works correctly - this
is a direct stub producer to seda consumer path without the nested multicast.
Key code path:
- {{camel-telemetry}}: {{Tracer.TracingRoutePolicy.onExchangeDone()}} ->
{{endEventSpan()}} -> {{spanLifecycleManager.deactivate(span)}} ->
{{otelSpan.end()}}
- {{camel-seda}}: {{SedaProducer.process()}} (InOnly path) ->
{{addToQueue(exchange, true)}} -> async consumer pickup
- {{camel-stub}}: extends seda, same behavior
h3. Impact
- Orphan spans in trace visualization (Jaeger, TUI waterfall) - processor spans
reference non-existent parent
- Missing routeId propagation for orphaned spans (dev console cannot walk
parent chain)
- Affects any scenario with stub/seda consumers inside multicast or other async
EIPs
h3. How this was discovered
Using the TUI MCP {{tui_get_spans}} tool to fetch raw span JSON data from a
running Camel TUI session, analyzing parent chain integrity across 20
consecutive traces (500 spans). Every trace showed the identical orphan pattern.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)