Hi

Thanks for the details.

If you don't call yourself but call another route that is similar then it
can continue.



On Tue, Feb 24, 2026 at 4:54 AM Dark Knight <[email protected]>
wrote:

> Thanks for the response!
>
> The sample I shared is just a reproducer. In the real scenario, we load
> millions of records from a database, transform them, and index them into
> SOLR. The SOLR document building stage runs with streaming and parallel
> processing enabled. We use the JDBC, SEDA, and SOLR components, along with
> the Splitter and Aggregate EIPs, to accomplish this.
>
> We are currently seeing significant CPU usage during document building, so
> we wanted to evaluate whether enabling exchange pooling would provide
> measurable performance or GC improvements. Since the splitter creates an
> exchange copy for each DB row, we thought enabling pooling could reduce the
> load from object allocation. To give a rough estimate, we process about 25M
> records.
>
> Regarding the recursive route, the SOLR document is a denormalized
> representation of data coming from normalized tables, and the processing
> involves nested queries and hierarchical enrichment steps. The self-calling
> route is used to handle these nested processing stages in a generic and
> reusable way.
>
> On Mon, Feb 23, 2026 at 3:02 PM Claus Ibsen <[email protected]> wrote:
>
> > Hi
> >
> > Ah sorry it is indeed the pooled exchanges and its more complex when
> > parallel and that you have recursive.
> > Turn off pooled and it works.
> >
> > What is the reason that you attempt to use pooled?
> >
> >
> >
> > On Mon, Feb 23, 2026 at 9:50 PM Claus Ibsen <[email protected]>
> wrote:
> >
> > > Hi
> > >
> > > No its not due to pooled, but due to parallel, so you have concurrent
> > > threads that trigger the splitter to prepare for splitting and then
> they
> > > may up in a thread safety issue.
> > > What you do is also weird and not common to recursive call the same
> > route.
> > >
> > >
> > >
> > > On Fri, Feb 20, 2026 at 8:13 PM Dark Knight <
> [email protected]>
> > > wrote:
> > >
> > >> Hi All,
> > >>
> > >> I’m observing an intermittent UnsupportedOperationException in
> Splitter
> > >> EIP
> > >> with parallel processing when the pooled exchange is enabled. The
> > scenario
> > >> is to flatten a nested list using a recursive route. The route has
> > >> streaming and parallel processing enabled.
> > >>
> > >>
> > >> The following exception is raised ~7 out of 10 runs
> > >>
> > >>
> > >> 11:32:17.179 [Camel (camel-1) thread #2 - Split] ERROR
> > >> org.apache.camel.processor.errorhandler.DefaultErrorHandler -- Failed
> > >> delivery for (MessageId: 6B26CA6266F8B1A-000000000000016E on
> ExchangeId:
> > >> 6B26CA6266F8B1A-000000000000016E). Exhausted after delivery attempt: 1
> > >> caught: java.lang.UnsupportedOperationException: Is this really
> correct
> > ?
> > >>
> > >> Message History (source location and message history is disabled)
> > >>
> > >>
> >
> ---------------------------------------------------------------------------------------------------------------------------------------
> > >> Source                                   ID
> > >> Processor                                          Elapsed (ms)
> > >>                                         route1/route1
> > >> from[direct://processList]                                    8
> > >> ...
> > >>                                         route1/split1
> > >> split[simple{${body}}]                                        0
> > >>
> > >> Stacktrace
> > >>
> > >>
> >
> ---------------------------------------------------------------------------------------------------------------------------------------
> > >> java.lang.UnsupportedOperationException: Is this really correct ?
> > >> at
> > >>
> > >>
> >
> org.apache.camel.processor.MulticastProcessor.wrapInErrorHandler(MulticastProcessor.java:1063)
> > >> at
> > >>
> > >>
> >
> org.apache.camel.processor.MulticastProcessor.createProcessorExchangePair(MulticastProcessor.java:1045)
> > >> at
> > >>
> > >>
> >
> org.apache.camel.processor.Splitter$SplitterIterable$1.next(Splitter.java:243)
> > >> at
> > >>
> > >>
> >
> org.apache.camel.processor.Splitter$SplitterIterable$1.next(Splitter.java:184)
> > >> at
> > >>
> > >>
> >
> org.apache.camel.processor.MulticastProcessor$MulticastReactiveTask.getNextProcessorExchangePair(MulticastProcessor.java:640)
> > >> at
> > >>
> > >>
> >
> org.apache.camel.processor.MulticastProcessor$MulticastReactiveTask.run(MulticastProcessor.java:557)
> > >> at
> > >>
> > >>
> >
> org.apache.camel.impl.engine.DefaultReactiveExecutor$Worker.doRun(DefaultReactiveExecutor.java:199)
> > >> at
> > >>
> > >>
> >
> org.apache.camel.impl.engine.DefaultReactiveExecutor$Worker.executeReactiveWork(DefaultReactiveExecutor.java:189)
> > >> at
> > >>
> > >>
> >
> org.apache.camel.impl.engine.DefaultReactiveExecutor$Worker.tryExecuteReactiveWork(DefaultReactiveExecutor.java:166)
> > >> at
> > >>
> > >>
> >
> org.apache.camel.impl.engine.DefaultReactiveExecutor$Worker.schedule(DefaultReactiveExecutor.java:148)
> > >> at
> > >>
> > >>
> >
> org.apache.camel.impl.engine.DefaultReactiveExecutor.scheduleSync(DefaultReactiveExecutor.java:64)
> > >> at
> > >>
> > >>
> >
> org.apache.camel.processor.MulticastProcessor.lambda$doProcess$0(MulticastProcessor.java:362)
> > >> at
> > >>
> > >>
> >
> java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
> > >> at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
> > >> at
> > >>
> > >>
> >
> java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
> > >> at
> > >>
> > >>
> >
> java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
> > >> at java.base/java.lang.Thread.run(Thread.java:833)
> > >>
> > >>
> > >> Test Code
> > >>
> > >>
> > >> public class FlattenListTest extends CamelTestSupport {
> > >>
> > >>    private static final int L1 = 20;
> > >>
> > >>    private static final int L2 = 5;
> > >>
> > >>    private static final int L3 = 5;
> > >>
> > >>    @Override
> > >>
> > >>    protected RoutesBuilder createRouteBuilder() {
> > >>
> > >>        return new RouteBuilder() {
> > >>
> > >>            @Override
> > >>
> > >>            public void configure() {
> > >>
> > >>            from("direct:processList")
> > >>
> > >>                .choice()
> > >>
> > >>                    .when(exchange -> (exchange.getIn().getBody()
> > >> instanceof
> > >> List))
> > >>
> > >>
> > .split(body()).streaming().parallelProcessing(true)
> > >>
> > >>                        .to("direct:processList")
> > >>
> > >>                    .endChoice()
> > >>
> > >>                    .otherwise()
> > >>
> > >>                        .log("${body}")
> > >>
> > >>                        .to("mock:result")
> > >>
> > >>                    .end();
> > >>
> > >>            }
> > >>
> > >>        };
> > >>
> > >>    }
> > >>
> > >>    @Override
> > >>
> > >>    protected CamelContext createCamelContext() throws Exception {
> > >>
> > >>        CamelContext camelContext = super.createCamelContext();
> > >>
> > >>        ExtendedCamelContext ecc =
> > camelContext.getCamelContextExtension();
> > >>
> > >>        ecc.setExchangeFactory(new PooledExchangeFactory());
> > >>
> > >>        ecc.setProcessorExchangeFactory(new
> > >> PooledProcessorExchangeFactory());
> > >>
> > >>        ecc.getExchangeFactory().setStatisticsEnabled(true);
> > >>
> > >>        ecc.getProcessorExchangeFactory().setStatisticsEnabled(true);
> > >>
> > >>        return camelContext;
> > >>
> > >>    }
> > >>
> > >>    @Test
> > >>
> > >>    public void testSplitter() throws Exception {
> > >>
> > >>        List<List<List<Integer>>> data = new ArrayList<>();
> > >>
> > >>        int num = 0;
> > >>
> > >>        for (int i = 0; i < L1; i++) {  // Outer level
> > >>
> > >>            List<List<Integer>> level2 = new ArrayList<>();
> > >>
> > >>            for (int j = 0; j < L2; j++) {  // Middle level
> > >>
> > >>                List<Integer> level3 = new ArrayList<>();
> > >>
> > >>                for (int k = 0; k < L3; k++) {  // Inner level
> > >>
> > >>                    level3.add(num++);
> > >>
> > >>                }
> > >>
> > >>                level2.add(level3);
> > >>
> > >>            }
> > >>
> > >>            data.add(level2);
> > >>
> > >>        }
> > >>
> > >>        getMockEndpoint("mock:result").expectedMessageCount(num);
> > >>
> > >>        template.sendBody("direct:processList", data);
> > >>
> > >>        MockEndpoint.assertIsSatisfied(context);
> > >>
> > >>    }
> > >>
> > >> }
> > >>
> > >>
> > >> The route should flatten the nested list and deliver all individual
> > >> elements to the final endpoint without exceptions or data loss. Please
> > let
> > >> me know if this is a known limitation or if I’m misusing pooled
> > exchanges
> > >> in this scenario.
> > >>
> > >>
> > >> Camel Version: 4.18.0
> > >>
> > >> Java Version: 17
> > >>
> > >
> > >
> > > --
> > > Claus Ibsen
> > >
> >
> >
> > --
> > Claus Ibsen
> >
>


-- 
Claus Ibsen

Reply via email to