[ 
https://issues.apache.org/jira/browse/CAMEL-26538?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Claus Ibsen updated CAMEL-26538:
--------------------------------
    Priority: Minor  (was: Major)

> camel-support - API components: stopping one CamelContext breaks the API 
> calls of the other CamelContexts (shared static thread pool)
> -------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: CAMEL-26538
>                 URL: https://issues.apache.org/jira/browse/CAMEL-26538
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-box, camel-google-drive, camel-support, 
> camel-zendesk
>            Reporter: shashank
>            Assignee: shashank
>            Priority: Minor
>
> Found with a TLA+ model of the thread pool that {{AbstractApiEndpoint}} 
> shares between the CamelContexts of a JVM, then reproduced with a test on the 
> real classes.
> The producers of the API components submit each call to a thread pool 
> ({{AbstractApiProducer.process}}: 
> {{endpoint.getExecutorService().submit(invocation)}}). This affects 11 
> components: as2, box, braintree, dhis2, fhir, google-calendar, google-drive, 
> google-mail, google-sheets, twilio and zendesk. olingo2 and olingo4 have 
> their own {{process}} and do not use the pool.
> The pool is kept in a static map, {{EXECUTOR_SERVICE_MAP}}, with one pool per 
> endpoint class for the whole JVM. The ExecutorServiceManager of the first 
> CamelContext that calls the API creates it, so that CamelContext shuts it 
> down when it stops. Each endpoint also keeps the pool in a field and never 
> checks it again ({{getExecutorService()}}: {{if (executorService == null)}}).
> When a JVM has two CamelContexts that use the same API component, stopping 
> the first one breaks the second one:
> * Calls of the second CamelContext that are still running when the shutdown 
> await time ends ({{shutdownAwaitTermination}}, 10 seconds by default) are 
> interrupted ({{shutdownNow}}).
> * Every later call of the second CamelContext is dropped. The default thread 
> pool profile uses {{CallerRuns}}, and the JDK {{CallerRunsPolicy}} discards a 
> task submitted to a pool that is shut down. So the call never runs, the 
> callback is never called, and the exchange never completes. A synchronous 
> caller (the thread of a synchronous consumer, {{template.request}}) waits for 
> good, and nothing is logged. With an {{Abort}} profile, the call fails with a 
> {{RejectedExecutionException}} instead.
> {noformat}
> 1 context A : sends to google-drive -> creates the pool (owned by A), puts it 
> in the static map
> 2 context B : sends to google-drive -> takes A's pool from the static map, 
> keeps it in its endpoint
> 3 context A : stop() -> A's ExecutorServiceManager shuts the pool down
> 4 context B : sends again -> submit() to the shut down pool -> CallerRuns 
> drops the task -> the exchange hangs
> {noformat}
> This happens with any two CamelContexts in one JVM, for example several 
> CamelContexts in one application, or one of several deployments on a shared 
> runtime being undeployed. An endpoint created after step 3 is not affected: 
> the static lookup sees that the pool is shut down and creates a new one. Only 
> the endpoints that kept the pool before the stop are broken, and the routes 
> of a running CamelContext keep their endpoints.
> An endpoint object that is kept across a stop and start of its own 
> CamelContext breaks the same way. For example, a route built with the 
> endpoint instance ({{to(endpoint)}}) keeps it, and that route's next call is 
> dropped. Routes that use a URI get a new endpoint when the CamelContext 
> starts again, so they are not affected.
> Reproduced with:
> * a stub API component in camel-core: step 4 never completes, a call running 
> during step 3 is interrupted, the endpoint kept across a restart never 
> completes its call, and endpoints of two CamelContexts that look up the pool 
> at the same time share pools across the CamelContexts (and one CamelContext 
> ends up with several pools);
> * camel-google-drive with a mock HTTP transport, where the send of the second 
> CamelContext after the first one stopped never completes.
> *Model.* A TLA+ model with two CamelContexts, one endpoint each, checks that 
> an exchange of a running CamelContext is never dropped and that stopping one 
> CamelContext never interrupts a call of another. Both fail on main (11-step 
> traces), and both also fail if the endpoint only re-checks the pool it kept 
> (one pool per JVM). They hold with one pool per CamelContext.
> *Proposal*
> * Keep the pools per CamelContext, for example in a context plugin that holds 
> one pool per endpoint class. Create each pool through that CamelContext's 
> ExecutorServiceManager, as now.
> * Let the endpoint look up the pool again when the one it kept was shut down. 
> This covers an endpoint that is used again after its CamelContext was started 
> again.
> * Each CamelContext then has its own pool. The size of each comes from the 
> thread pool profile of the component (such as {{CamelGoogleDrive}}) or the 
> default profile of its own CamelContext. Add an upgrade guide note.
> _Filed with Claude Code on behalf of allthingssecurity._



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to