MultiIndexMergeScheduler is listed under Lucene 10.3.0 New Features as 'a production multi-tenant merge scheduler' and its javadoc says to create one instance per index, but the class is package-private, so application code cannot compile against it or construct it through supported means
Finding LFV-885Area: core / index merge scheduling (MultiIndexMergeScheduler visibility)Priority: lowImpact: minorNovelty: new
AI-assisted finding, confirmed by automated checks, not yet triaged by a human Lucene committer. This site files nothing upstream.
What it shows
You read the Lucene 10.3.0 release notes ('MultiIndexMergeScheduler: a production multi-tenant merge scheduler') and the class javadoc ('can be used easily by creating a MultiIndexMergeScheduler instance for each index'), and write iwc.setMergeScheduler(new MultiIndexMergeScheduler(dir)) in your application, using the public constructor the javadoc calls 'The main MultiIndexMergeScheduler constructor -- use this one'. You expect it to compile, as the same line with ConcurrentMergeScheduler does. Instead javac rejects it because the class itself is declared 'class MultiIndexMergeScheduler' without 'public' (MultiIndexMergeScheduler.java:36), and it has been package-private in every release that contains it, so only code inside org.apache.lucene.index (in practice Lucene's own test) can use it.
The test shows that application code (any package other than org.apache.lucene.index) cannot create the MultiIndexMergeScheduler that the 10.3.0 CHANGES entry advertises and whose javadoc says to create once per index: the class is package-private.
Expected vs actual
Expected: the test compiles and passes, as with new ConcurrentMergeScheduler(). Actual: javac fails with MultiIndexMergeScheduler is not public in org.apache.lucene.index; cannot be accessed from outside package.
Files
Run it
- Tested on Lucene main e1faeba08bde (2026-11-02): still reproduces.
- Tested on Lucene main 645d4fe14a20 (2026-10-28): reproduced.
- How to read the outcome: a failing test is the finding reproducing (the failure message states expected against actual).
- Seeds: a rerun with the same seed replays the same random choices. Assertions off (-da -dsa -Dtests.asserts=false) is the production-like setting.
# 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.example.TestMultiIndexMergeSchedulerFromApplication
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.