[
https://issues.apache.org/jira/browse/CAMEL-26578?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Claus Ibsen resolved CAMEL-26578.
---------------------------------
Resolution: Fixed
Fixed by https://github.com/apache/camel/pull/29194 (merged to main). _Claude
Code on behalf of Claus Ibsen (davsclaus)._
> camel-core - ProducerTemplate: the future of an async send or request never
> completes when the template is stopped before the task ran
> --------------------------------------------------------------------------------------------------------------------------------------
>
> Key: CAMEL-26578
> URL: https://issues.apache.org/jira/browse/CAMEL-26578
> Project: Camel
> Issue Type: Bug
> Components: camel-core
> Reporter: shashank
> Assignee: shashank
> Priority: Major
> Fix For: 4.24.0
>
>
> Found with Fray (a controlled concurrency testing tool for the JVM) on a test
> that stops a {{ProducerTemplate}} while an {{asyncRequestBody}} is pending;
> it failed at the third schedule, and the latch-based JUnit tests below
> reproduce it deterministically.
> {{DefaultProducerTemplate.asyncSendExchange}} creates the
> {{CompletableFuture}} it returns and {{submit}}s a lambda to the template
> executor that calls {{getProducerCache().asyncSendExchange(..., future)}}.
> Nobody completes that future when the lambda does not get to the producer
> cache:
> # *The task is still queued when the template stops.* {{doStop}} calls
> {{ExecutorServiceManager.shutdownNow(executor)}}, which drops the queued
> tasks; their futures are never completed.
> # *The task runs after the stop* (an executor that does not drop it, such as
> a custom one set with {{setExecutorService}}): {{getProducerCache()}} throws
> {{IllegalStateException("ProducerTemplate has not been started")}} inside the
> lambda, {{submit()}} keeps it in its own {{FutureTask}} which nobody reads,
> and the returned future is never completed.
> # *Anything else that throws before the producer cache's own try/catch*, e.g.
> {{Endpoint.createExchange}} failing in
> {{DefaultProducerCache.asyncSendExchange}}: same as 2.
> A caller in {{future.get()}} without a timeout waits for ever;
> {{thenApply}}/{{whenComplete}} stages never run.
> {{DefaultFluentProducerTemplate.asyncSend()}}/{{asyncRequest()}} go through
> the same method.
> Short trace (case 1): caller {{asyncRequestBody("direct:start", body)}} ->
> task queued behind a busy executor thread -> another thread
> {{template.stop()}} -> {{shutdownNow}} returns the task, which is dropped ->
> {{future.get()}} never returns.
> {code:java}
> ProducerTemplate producer = context.createProducerTemplate();
> ExecutorService single =
> context.getExecutorServiceManager().newSingleThreadExecutor(this, "test");
> producer.setExecutorService(single);
> single.execute(() -> await(release)); // keep the only thread busy
> Future<Object> f = producer.asyncRequestBody("direct:start", "Hello World");
> producer.stop(); // shutdownNow drops the
> queued task
> release.countDown();
> f.get(); // never returns
> {code}
> The same happens with the default executor when the template is stopped while
> more async requests are queued than the pool has threads. (Not covered here:
> a template that is not stopped itself, whose pool the ExecutorServiceManager
> shuts down when the CamelContext stops; its queued tasks are dropped after
> the shutdown await while the template stays started.)
> Fix idea: send through a small {{Runnable}} that holds the future and
> completes it exceptionally when sending throws; keep the tasks that have not
> started in a set and complete their futures exceptionally (with a
> {{RejectedExecutionException}}) in {{doStop}} after the executor is shut down
> (also a task handed over while the template is stopping). Sent exchanges and
> the futures of requests that run are unchanged.
> _Filed with Claude Code on behalf of allthingssecurity._
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)