[ 
https://issues.apache.org/jira/browse/CAMEL-23380?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18077118#comment-18077118
 ] 

AJ Danelz commented on CAMEL-23380:
-----------------------------------

I am also working on this problem on the team with [~nikita]. This is an Otel 
error we see in the logs:

```
Scope garbage collected before being closed. java.lang.AssertionError: Thread 
[http-nio-8080-exec-16] opened a scope of 
\{opentelemetry-trace-span-key=SdkSpan{traceId=69f2373a6c375a4cce4ec31ff5f48ba8,
 spanId=8950735d7653e2af, 
parentSpanContext=ImmutableSpanContext{traceId=69f2373a6c375a4cce4ec31ff5f48ba8,
 spanId=47ecab290cd88b3b, traceFlags=01, 
traceState=ArrayBasedTraceState{entries=[]}, remote=true, valid=true}, 
name=corsair/carrier-direct-tracking, kind=INTERNAL, 
attributes=AttributesMap\{data={url.scheme=rest, http.method=POST, 
http.url=http://x1-adapter-corsair/api/corsair/carrier-direct-tracking, 
camel.uri=rest://post:corsair/carrier-direct-tracking?consumerComponentName=servlet&consumes=application%2Fjson&produces=application%2Fjson&routeId=carrier-direct-tracking-rest-consumer-a4f47aa5-aef6-4f70-b50f-66c3d9887eb8--rest,
 exchangeId=D96E48AE9F44E8D-00000000000000D2, op=EVENT_RECEIVED, 
http.status_code=500, component=camel-rest, 
url.query=consumerComponentName=servlet&consumes=application%2Fjson&produces=application%2Fjson&routeId=carrier-direct-tracking-rest-consumer-a4f47aa5-aef6-4f70-b50f-66c3d9887eb8--rest,
 url.path=post:corsair/carrier-direct-tracking}, capacity=128, 
totalAddedValues=10}, status=ImmutableStatusData\{statusCode=UNSET, 
description=}, totalRecordedEvents=0, totalRecordedLinks=0, 
startEpochNanos=1777481530565640411, endEpochNanos=1777481531086993069}, 
opentelemetry-baggage-key=\{camelScope=ImmutableEntry{value=true, 
metadata=ImmutableEntryMetadata{value=}}}, 
otelTraceContext=ImmutableSpanContext\{traceId=69f2373a6c375a4cce4ec31ff5f48ba8,
 spanId=f8f9d7fcf7d12157, traceFlags=01, 
traceState=ArrayBasedTraceState{entries=[]}, remote=false, valid=true}} here: 
at 
io.micrometer.tracing.otel.bridge.EventPublishingContextWrapper$1.attach(EventPublishingContextWrapper.java:50)
 at 
org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryEventPublisherBeansApplicationListener$Wrapper$Storage.attach(OpenTelemetryEventPublisherBeansApplicationListener.java:180)
 at io.opentelemetry.context.Context.makeCurrent(Context.java:231) at 
io.opentelemetry.context.ImplicitContextKeyed.makeCurrent(ImplicitContextKeyed.java:33)
 at 
org.apache.camel.opentelemetry2.OpenTelemetrySpanAdapter.makeCurrent(OpenTelemetrySpanAdapter.java:51)
 at 
org.apache.camel.opentelemetry2.OpenTelemetryTracer$OpentelemetrySpanLifecycleManager.activate(OpenTelemetryTracer.java:144)
 at org.apache.camel.telemetry.Tracer.beginEventSpan(Tracer.java:276) at 
org.apache.camel.telemetry.Tracer$TracingRoutePolicy.onExchangeBegin(Tracer.java:229)
 at 
org.apache.camel.impl.engine.CamelInternalProcessor$RoutePolicyAdvice.before(CamelInternalProcessor.java:562)
 at 
org.apache.camel.impl.engine.CamelInternalProcessor.process(CamelInternalProcessor.java:332)
 at 
org.apache.camel.impl.engine.DefaultAsyncProcessorAwaitManager.process(DefaultAsyncProcessorAwaitManager.java:81)
 at 
org.apache.camel.support.AsyncProcessorSupport.process(AsyncProcessorSupport.java:32)
 at org.apache.camel.http.common.CamelServlet.doExecute(CamelServlet.java:313) 
at org.apache.camel.http.common.CamelServlet.doService(CamelServlet.java:235) 
at 
org.apache.camel.http.common.CamelServlet.handleService(CamelServlet.java:111) 
at org.apache.camel.http.common.CamelServlet.service(CamelServlet.java:97) at 
jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) at 
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
 at 
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138)
 at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) at 
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162)
 at 
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138)
 at 
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:110)
 at 
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162)
 at 
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138)
 at 
org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108)
 at 
org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108)
 at 
org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:490)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:351)
 at 
org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:83)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:129)
 at 
org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138)
 at 
org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125)
 at 
org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138)
 at 
org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138)
 at 
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138)
 at 
org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138)
 at 
org.springframework.security.web.authentication.AuthenticationFilter.doFilterInternal(AuthenticationFilter.java:177)
 at 
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138)
 at 
org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter.doFilterInternal(BearerTokenAuthenticationFilter.java:158)
 at 
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138)
 at 
com.rygen.x1.integrations.shared.config.KeyBasedEndpointAuthFilter.doFilterInternal(KeyBasedEndpointAuthFilter.java:70)
 at 
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138)
 at 
org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107)
 at 
org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138)
 at 
org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:117)
 at 
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138)
 at 
org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)
 at 
org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)
 at 
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138)
 at 
org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82)
 at 
org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138)
 at 
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62)
 at 
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:228)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138)
 at 
org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42)
 at 
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:241)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:334)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:225)
 at 
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:138)
 at 
org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233)
 at 
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191)
 at 
org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113)
 at 
org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52)
 at 
org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113)
 at 
org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74)
 at 
org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319)
 at 
org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113)
 at 
org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267)
 at 
org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113)
 at 
org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74)
 at 
org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240)
 at 
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362)
 at 
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278)
 at 
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162)
 at 
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138)
 at 
org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
 at 
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
 at 
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162)
 at 
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138)
 at 
org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
 at 
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
 at 
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162)
 at 
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138)
 at 
org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:110)
 at 
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
 at 
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162)
 at 
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138)
 at 
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
 at 
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
 at 
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162)
 at 
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138)
 at 
com.rygen.x1.integrations.shared.config.ConcurrencyLimitFilter.doFilterInternal(ConcurrencyLimitFilter.java:77)
 at 
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
 at 
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162)
 at 
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138)
 at 
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:165)
 at 
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:88)
 at 
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:492)
 at 
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:113) 
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:83) 
at 
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:72)
 at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:733) at 
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) at 
org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399) at 
org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
 at 
org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903)
 at 
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1797)
 at 
org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) 
at 
org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:973)
 at 
org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:491)
 at 
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)
 at java.base/java.lang.Thread.run(Thread.java:1474)
```

> `TracingRoutePolicy.onExchangeBegin` opens scopes and spans that are never 
> closed/ended
> ---------------------------------------------------------------------------------------
>
>                 Key: CAMEL-23380
>                 URL: https://issues.apache.org/jira/browse/CAMEL-23380
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-opentelemetry
>    Affects Versions: 4.14.5
>            Reporter: Nikita
>            Priority: Major
>
> *Summary*
> `Tracer$TracingRoutePolicy.onExchangeBegin()` calls `beginEventSpan()` which 
> opens an OTel scope via `makeCurrent()`, but the corresponding 
> `endEventSpan()` is never reached. The span is never ended 
> (`endEpochNanos=0`) and the scope is never closed. On pooled Tomcat threads, 
> this causes `ArrayBasedContext` objects to accumulate in `ThreadLocal` 
> storage, growing ~8 MB/hour until OOM.
> *Evidence*
> With `-Dio.opentelemetry.context.enableStrictContext=true` enabled, *{*}71 
> `Scope garbage collected before being closed` errors{*}* were captured over 
> 15 minutes of normal traffic. All 71 share an identical stack trace and all 
> 71 leaked spans have `endEpochNanos=0`.
> *{*}Stack trace (all 71 entries):{*}*
> ```
> io.opentelemetry.context.Context.makeCurrent (Context.java:231)
> io.opentelemetry.context.ImplicitContextKeyed.makeCurrent 
> (ImplicitContextKeyed.java:33)
> org.apache.camel.opentelemetry2.OpenTelemetrySpanAdapter.makeCurrent 
> (OpenTelemetrySpanAdapter.java:47)
> org.apache.camel.opentelemetry2.OpenTelemetryTracer$OpentelemetrySpanLifecycleManager.activate
>  (OpenTelemetryTracer.java:141)
> org.apache.camel.telemetry.Tracer.beginEventSpan (Tracer.java:267)
> org.apache.camel.telemetry.Tracer$TracingRoutePolicy.onExchangeBegin 
> (Tracer.java:220)
> org.apache.camel.impl.engine.CamelInternalProcessor$RoutePolicyAdvice.before 
> (CamelInternalProcessor.java:554)
> org.apache.camel.impl.engine.CamelInternalProcessor.process 
> (CamelInternalProcessor.java:324)
> ```
> *{*}Per-exchange pattern:{*}*
>  - 41 exchanges produced 71 leaks
>  - 26 exchanges leaked 2 scopes: `[router-route span, child-route span]`
>  - 15 exchanges leaked 3 scopes: `[router-route span, router-route span, 
> child-route span]`
> All 71 leaks occur on the same Tomcat thread as the originating exchange (no 
> async/cross-thread handoff for the HTTP side)
> The double router-route leak in the 3-scope pattern may indicate 
> `TracingRoutePolicy` is registered twice on some routes, but the core issue 
> (spans never ended) affects all patterns.
> *{*}Additional observation — wireTap executor threads also affected:{*}*
> Heap dump analysis from Apr 29 shows `wireTapIoExecutor` threads accumulating 
> OTel contexts via the same ThreadLocal mechanism. In the most leaked pod 
> observed (879 MB live heap), two of the four wireTap executor threads each 
> retained ~15.9 MB of OTel context chains while the other two remained at 
> baseline (~147 KB). The selectively affected threads are those that have 
> processed exchanges on the child handler routes (route 3 above), which 
> contain multiple `.wireTap()` calls. The wireTap executor threads are not 
> Tomcat servlet threads, so the leak on these threads follows a different call 
> path than the HTTP side but appears to share the same root cause: 
> `TracingRoutePolicy.onExchangeBegin` opening a scope that `endEventSpan` 
> never closes.
> *{*}Span names from the leaking routes:{*}*
> |Leaks|Span Name|
> |—|—|
> |31|`corsair/carrier-direct-documents` (child route)|
> |18|`carrier-direct-tracking-rest-consumer-*-router` (router route)|
> |12|`carrier-direct-document-rest-consumer-*-router` (router route)|
> |10|`corsair/carrier-direct-tracking` (child route)|
> *Route structure*
> The leaking routes follow a standard REST DSL pattern with three routes per 
> endpoint:
> 1. `rest:post:/path` (auto-generated, `routeId = X--rest`) calls 
> `.to("direct:X-router")`
> 2. `direct:X-router` (`routeId = X-router`) dispatches via 
> `toD("direct:${header.ROUTE_TO}")`
> 3. `direct:X-<version>` (`routeId = X::<version>`) is the handler with 
> multiple `.wireTap()` calls
> Spans for routes 2 and 3 leak. Route 1's span does not appear in the 
> strict-context output.
> *Heap dump confirmation*
> Production heap dumps across multiple pods and dates show linear growth of 
> OTel objects retained by Tomcat `http-nio-*-exec` ThreadLocals, with 
> `wireTapIoExecutor` threads also affected at higher uptimes:
> |Date|Uptime|TaskThread Retained|Live Heap (MAT)|`SdkSpan` Count|
> |—|—|—|—|—|
> |Apr 17|0.2h|not measurable|~470 MB (baseline)|—|
> |Apr 17|10.8h|104 MB (16.8%)|593 MB|23,188|
> |Apr 17|23.9h|201 MB (26.2%)|733 MB|47,145|
> |Apr 24|23.9h|209 MB (30.1%)|697 MB|51,444|
> |Apr 29|high uptime|*{*}309 MB (33.5%){*}*|*{*}879 MB{*}*|*{*}83,138{*}*|
> Growth rate: ~8 MB/hour across HTTP threads. With an 8 GB max heap, pods OOM 
> after approximately 40-50 hours. `SdkSpan` count (unended spans) grows 
> proportionally and serves as the most direct indicator of leak severity.
> *Configuration*
> ```yaml
> camel:
>   opentelemetry2:
>     enabled: true
>     trace-processors: false   # only route-level spans, no per-processor spans
> ```
> *Environment*
> |Dependency|Version|
> |—|—|
> |`camel-opentelemetry2-starter`|4.14.5|
> |`camel-telemetry`|4.14.5 (transitive)|
> |`io.opentelemetry:opentelemetry-bom`|1.60.1|
> |`io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom`|2.26.1|
> |`io.micrometer:micrometer-tracing-bridge-otel`|1.5.10|
> |Spring Boot|3.5.13|
> |JDK|25.0.1+8-LTS|
> Note: `CAMEL-21302` (async producer context leak, fixed in 4.4.5) does not 
> cover this case. Our leak is synchronous, same-thread, and caused by 
> `beginEventSpan`/`endEventSpan` lifecycle mismatch rather than cross-thread 
> scope closure.
> *Expected behavior*
> Every `beginEventSpan()` call in `TracingRoutePolicy.onExchangeBegin` should 
> have a matching `endEventSpan()` that ends the span and closes the scope when 
> the exchange completes on that route.
> *Workaround*
> A servlet filter calling `Context.root().makeCurrent().close()` after each 
> request caps the heap impact on Tomcat HTTP threads but does not fix the 
> underlying issue (spans still go unended, trace data is lost). It also has no 
> effect on `wireTapIoExecutor` threads or other non-servlet thread pools, 
> which accumulate OTel contexts via the same mechanism but are not covered by 
> the filter chain.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to