shashank created CAMEL-25506:
--------------------------------
Summary: camel-lucene - concurrent queries can get another query's
hits, and queries fail after the route is restarted
Key: CAMEL-25506
URL: https://issues.apache.org/jira/browse/CAMEL-25506
Project: Camel
Issue Type: Bug
Components: camel-lucene
Reporter: shashank
{{LuceneQueryProducer}} creates one {{LuceneSearcher}} in {{doStart()}} and
uses it for every exchange (Camel producers are shared by concurrent
exchanges). {{LuceneSearcher}} keeps the state of a query in fields (main
c578a42a776d): {{open()}} assigns {{indexReader}} and {{indexSearcher}} (lines
47-51) and {{doSearch()}} assigns {{hits}} (line 94), which {{search()}} then
iterates (line 71). So:
* two exchanges querying at the same time can get each other's results: one
exchange assigns {{hits}} between the other's search and its loop over
{{hits}}. In the test below, 27 to 48 of 20000 queries (8 threads, two terms)
returned the hits of the other term on main;
* every query opens a new {{DirectoryReader}} and only the one in the field
when the producer stops is closed; the others are left to the garbage collector;
* {{LuceneQueryProducer.doStop()}} calls {{LuceneSearcher.close()}}, which
closes the endpoint's {{Analyzer}} (the configured bean or the endpoint's
default). After the route is stopped and started again, every query fails with
{{org.apache.lucene.store.AlreadyClosedException: this Analyzer is closed}}
until the CamelContext is restarted; when the route had not run a query yet,
the stop fails with a {{NullPointerException}} ({{indexReader}} is null),
logged as a WARN by the shutdown strategy.
{{LuceneQueryProcessor}} has the same reader handling (a new searcher in a
shared field per exchange, never closed).
h3. Reproduction
New {{LuceneQueryProducerLifecycleTest}} (an index of 20 "alpha" and 20 "beta"
documents built in {{@BeforeEach}}, a route {{direct:query}} to
{{lucene:queryIndex:query}}):
* {{queryWorksAfterRouteRestart}} (deterministic): on main {{a query after the
route was restarted must succeed ==> expected: <null> but was:
<org.apache.lucene.store.AlreadyClosedException: this Analyzer is closed>}}.
* {{concurrentQueriesGetTheirOwnHits}} (20000 queries on 8 threads, about 2 s;
it cannot force the interleaving, it makes it very likely): on main {{each
query must get the hits of its own query, but 39 of 20000 did not, e.g. [alpha:
got hit 'beta document 0', ...]}}; it failed in all six runs on main (27 to 48
wrong answers) and passed in every run with the fix.
h3. Proposed fix
Each exchange uses its own {{LuceneSearcher}} ({{open}}, {{search}}, {{close}}
in a {{finally}}); the producer no longer keeps one, so {{doStart}}/{{doStop}}
are gone. {{LuceneSearcher.close()}} closes only its reader (null-safe), not
the analyzer, which belongs to the endpoint or the registry.
{{LuceneQueryProcessor}} does the same (no dedicated test;
{{LuceneQueryProcessorIT}} passes). Code that calls {{LuceneSearcher}} directly
now has to close its analyzer itself. The {{Hits}} hold the stored values (and
the {{Document}} when requested), so closing the reader after building them is
safe; the existing ITs still pass. camel-lucene tests with the fix:
{{LuceneQueryProducerLifecycleTest}} 2, {{LuceneIndexAndQueryProducerIT}} 4,
{{LuceneQueryProcessorIT}} 2, 0 failures.
Found with a TLA+ model of the producer and searcher (one action per field
read/write: open reader, create searcher, collect hits, iterate hits; route
stop/start): "every answer holds the hits of its own query" is violated in 7
steps with two threads, "no reader is left open after the stop" in 12 steps
with one thread and two queries, and "no query runs on a closed analyzer" in 9
steps (query, stop, start, query). With the fix all properties hold for 2 and 3
threads and a restart. Then confirmed with the real component as above.
Affected: main, camel-4.22.x, camel-4.18.x, camel-4.14.x (same code, GitHub
contents API; unchanged since 2.x).
Duplicate check (2026-10-09): JIRA component camel-lucene (8 issues, all
upgrades or naming), text "LuceneSearcher" (4, upgrades), "this Analyzer is
closed" (none); GitHub pull requests "camel-lucene": none about the producers.
_Filed with Claude Code on behalf of allthingssecurity._
--
This message was sent by Atlassian Jira
(v8.20.10#820010)