GITHUB#7820: refuse to exorcise an index whose commit point could not be read - #16474
Open
serhiy-bzhezytskyy wants to merge 1 commit into
Open
GITHUB#7820: refuse to exorcise an index whose commit point could not be read#16474serhiy-bzhezytskyy wants to merge 1 commit into
serhiy-bzhezytskyy wants to merge 1 commit into
Conversation
…able CheckIndex#exorciseIndex dereferenced Status#newSegments after checking only Status#partial. When the commit point itself cannot be read, checkIndex leaves newSegments null and sets missingSegments instead, so the public method threw NullPointerException: Cannot invoke SegmentInfos.changed() because result.newSegments is null. Three corruptions reach it, and each is one users report on this issue: a deleted .si, a truncated .si, and a truncated segments_N. All three produce clean=false with missingSegments=true, and all three failed at the same line. CheckIndex's own command line is not affected, since doCheck returns 1 on missingSegments. Luke is: CheckIndexDialogFactory enables its Repair button on !status.clean and reads missingSegments only when building the result text, and IndexUtils#tryRepairIndex calls exorciseIndex with no check of its own, so a Luke user who checks an index with an unreadable commit point gets an enabled Repair button and an NPE when they press it. Mike McCandless warned about exactly this when skipMissing was added in 2015: "if accidentally mis-used it can make you think you have all segments if you forget to check missingSegmentInfo". This does not implement step 2 of the issue. Recovering these indexes still needs the thrown exception to carry which segment is broken, which is the work described in the November 2023 comment. Until then the caller gets an IllegalArgumentException explaining that nothing can be recovered, rather than an NPE. The test covers all three corruptions and sits next to testPriorBrokenCommitPoint, which is step 1 of the same issue; exorciseIndex had no test coverage at all before this.
This was referenced Aug 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
CheckIndex#exorciseIndexdereferencesStatus#newSegmentsafter checking onlyStatus#partial. When the commit point cannot be read,checkIndexleavesnewSegmentsnull and setsmissingSegmentsinstead, so the public method fails with:Reproduced on
mainwith three corruptions, each of them one that users report on #7820:DirectoryReader.opencheckIndexexorciseIndex_1.sideletedCorruptIndexExceptionclean=false missingSegments=true_1.sitruncatedCorruptIndexExceptionsegments_NtruncatedCorruptIndexExceptionThe command line is not affected —
doCheckhasif (result.missingSegments) return 1;atCheckIndex.java:4880. Luke is:CheckIndexDialogFactoryenables its Repair button onif (!st.clean)(:304) and readsmissingSegmentsonly when building the result text (:329), whileIndexUtils#tryRepairIndex(:318) callsci.exorciseIndex(st)with no check of its own. So a Luke user who checks an index with an unreadable commit point gets an enabled Repair button and an NPE when they press it.This is what @mikemccand warned about when
skipMissingwas proposed on #7820 in 2015:This change does not implement the repair that #7820 asks for. It only makes the failure legible until that exists, and the guard is symmetric with the
partialcheck already in the method.The test
@rmuir asked for exactly this on #12530 in August 2023:
@mikemccand agreed in the same thread — "It's terrifying to have zero test coverage for such a dangerous feature!".
grep exorciseIndex lucene/core/src/test/still returns nothing onmain, sotestExorciseUnreadableCommitPointis the first test that calls the method at all. It covers all three corruptions and sits next totestPriorBrokenCommitPoint, which is step 1 of the same issue.Relationship to #12872
@gokaai's PR #12872 is open since 2023-12 and its description says it is "a step towards enabling #7820". Its author also wrote there:
That is what this PR does, so it is not a competing change — it is the piece deferred there, and it stands whether or not #12872 lands. If it is easier to fold this into that PR instead, I will close this one. I am not proposing to take over step 2.
Verification
AssertionFailedError: Unexpected exception type, expected IllegalArgumentException but got java.lang.NullPointerException./gradlew :lucene:core:test --tests TestCheckIndexpasses./gradlew :lucene:core:check -x testpasses./gradlew tidyreports no changesVerified on
mainonly; the 10.x branches are not checked.