Federico Mariani created CAMEL-24155:
----------------------------------------
Summary: camel-salesforce - streamQueryResult iterator hangs the
route thread forever when queryMore fails
Key: CAMEL-24155
URL: https://issues.apache.org/jira/browse/CAMEL-24155
Project: Camel
Issue Type: Bug
Components: camel-salesforce
Reporter: Federico Mariani
Attachments: QueryResultIteratorErrorTest.java
Found during a code review of camel-salesforce on main (4.22.0-SNAPSHOT).
In {{QueryResultIterator.next()}} (internal/processor, lines ~60-94), the
page-fetch callback passed to {{restClient.queryMore(...)}} completely ignores
its {{exception}} parameter. On any failure,
{{AbstractClientBase.doHttpRequest}} invokes the callback with {{response ==
null}} plus a {{SalesforceException}}; {{objectMapper.readValue(null, ...)}}
then throws inside the Jetty worker thread, so {{latch.countDown()}} — which is
not in a finally block — is never reached, and {{latch.await()}}, which has *no
timeout*, blocks the consuming route thread forever. The same hang occurs when
{{readValue}} throws {{IOException}} or a page unexpectedly contains zero
records.
*Failure scenario*: {{streamQueryResult=true}} query over a large result set;
while the route iterates the returned Iterator, the session expires, the
network blips, or Salesforce returns any non-2xx for the {{nextRecordsUrl}}
fetch. The route thread blocks permanently inside {{next()}}, the exchange
never completes, and the only trace is an uncaught-exception log on a Jetty
worker thread.
*Reproducer*: attached {{QueryResultIteratorErrorTest.java}} (unit test, goes
under
{{components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/internal/processor/}}).
It simulates the asynchronous callback invocation exactly as production does
(callback fired from a separate thread with a null response and a
SalesforceException) and fails by preemptive timeout because {{next()}} never
returns.
*Suggested fix*: in the callback, check {{exception != null}} (and wrap parse
failures), store the error in a holder, always {{countDown()}} in a finally
block, and rethrow the stored error from {{next()}} on the iterating thread.
_This issue was researched and filed by Claude Code (AI) on behalf of Federico
Mariani (fmariani). Full findings document from the review is attached to the
umbrella issues._
--
This message was sent by Atlassian Jira
(v8.20.10#820010)