On main, the javadocs of LRUQueryCache.clearQuery and clearCoreCacheKey still say 'Remove all cache entries', but since #15558 both methods only mark the entries for the next cleanUp(), so the entries stay counted and keep producing cache hits until then; MIGRATE.md does not mention the deferred removal or the new cleanUp() duty (documentation defect, main only)

Finding LFV-945Area: search / query cache (LRUQueryCache method javadocs and MIGRATE.md)Priority: lowImpact: minorNovelty: new

Finding page for LFV-945

AI-assisted finding, confirmed by automated checks, not yet triaged by a human Lucene committer. This site files nothing upstream.

What it shows

On main you install an LRUQueryCache, run a query so that it is cached, and call clearQuery(query) or clearCoreCacheKey(key), whose javadocs say 'Remove all cache entries for the given query' and '... for the given core cache key'. You expect the entries to be gone when the call returns, as on 10.x; instead #15558 made both methods only add the query or key to a pending set, so getCacheSize() still counts the entry and the next search is a cache hit (with correct documents) until cleanUp() runs. The deferral is deliberate and the class javadoc describes it, but the two method javadocs were left unchanged, and MIGRATE.md, whose 11.0 recipe builds a cache with no cleanup, never mentions the new cleanUp() or CacheCleanUpParameters duty.

The test shows that on main LRUQueryCache.clearQuery(q) and clearCoreCacheKey(key) leave the cache entry in place until cleanUp(), although their javadocs still say "Remove all cache entries for the given query" / "... core cache key" (a documentation defect; the deferral itself is the intended #15558 design).

Expected vs actual

Expected (method javadocs): cache size 0 after each call. Actual: cache size after clearQuery / after clearCoreCacheKey expected:<[0 / 0]> but was:<[1 / 1]> (-ea and -da).

Files

Run it

# Lucene checkout at the tested commit, built with: ./gradlew --no-daemon assemble   (JDK 25)
LUCENE=/path/to/lucene; V=12.0.0-SNAPSHOT
CP=$LUCENE/lucene/core/build/libs/lucene-core-$V.jar
CP=$CP:$LUCENE/lucene/codecs/build/libs/lucene-codecs-$V.jar
CP=$CP:$LUCENE/lucene/test-framework/build/libs/lucene-test-framework-$V.jar
CP=$CP:randomizedtesting-runner-2.9.2.jar:junit-4.13.2.jar:hamcrest-3.0.jar   # test-framework dependencies (Maven Central)
CP=$CP:junit-platform-console-standalone-6.1.3.jar   # JUnit Platform console launcher (Maven Central)

javac -d classes -cp "$CP" $(find src -name '*.java')

# run 1: assertions on, seed DEADBEEF
java -ea -esa --add-modules jdk.incubator.vector --enable-native-access=ALL-UNNAMED -Dtests.seed=DEADBEEF \
     -cp "classes:$CP" org.junit.platform.console.ConsoleLauncher \
     execute --select-class org.apache.lucene.search.TestLRUQueryCacheClearRemovesEntries

Gradle alternative: copy the test into the matching module's src/test/ tree of the checkout and run ./gradlew -p lucene/<module> test --tests <class> -Ptests.seed=<seed> (add -Ptests.asserts=false for a run without assertions).

Feedback

Open an issue in lucene-ai-bug-hunt. Please do not open issues or pull requests in apache/lucene about this finding unless a Lucene committer agrees it should be filed.

Snapshot of 2026-11-04. AI-assisted; not yet triaged by a human Lucene committer. Feedback: open an issue in lucene-ai-bug-hunt.