mirror of https://github.com/apache/lucene.git
LUCENE-10428: Avoid infinite loop under error conditions. (#711)
Co-authored-by: dblock <dblock@dblock.org>
This commit is contained in:
parent
bb10e62dff
commit
0d35e38b93
|
@ -253,6 +253,10 @@ Bug Fixes
|
|||
* LUCENE-10405: When using the MemoryIndex, binary and Sorted doc values are stored
|
||||
as BytesRef instead of BytesRefHash so they don't have a limit on size. (Ignacio Vera)
|
||||
|
||||
* LUCENE-10428: Queries with a misbehaving score function may no longer cause
|
||||
infinite loops in their parent BooleanQuery.
|
||||
(Ankit Jain, Daniel Doubrovkine, Adrien Grand)
|
||||
|
||||
Other
|
||||
---------------------
|
||||
|
||||
|
|
|
@ -128,25 +128,40 @@ final class MaxScoreSumPropagator {
|
|||
}
|
||||
}
|
||||
|
||||
/** Return the minimum score that a Scorer must produce in order for a hit to be competitive. */
|
||||
/**
|
||||
* Return the minimum score that a Scorer must produce in order for a hit to be competitive.
|
||||
*
|
||||
* <p>The way that boolean queries combine scores of their sub clauses together is by summing up
|
||||
* the float scores into a double and finally casting back that double back to a float. This
|
||||
* method undoes this operation by taking the float score sum and subtracting the sum of other
|
||||
* scores as a double as a first approximation of the minimum score that this clause must have.
|
||||
*/
|
||||
private float getMinCompetitiveScore(float minScoreSum, double sumOfOtherMaxScores) {
|
||||
assert numClauses > 0;
|
||||
if (minScoreSum <= sumOfOtherMaxScores) {
|
||||
return 0f;
|
||||
}
|
||||
|
||||
// We need to find a value 'minScore' so that 'minScore + sumOfOtherMaxScores <= minScoreSum'
|
||||
// TODO: is there an efficient way to find the greatest value that meets this requirement?
|
||||
float minScore = (float) (minScoreSum - sumOfOtherMaxScores);
|
||||
int iters = 0;
|
||||
while (scoreSumUpperBound(minScore + sumOfOtherMaxScores) > minScoreSum) {
|
||||
for (int iter = 0;
|
||||
minScore > 0 && scoreSumUpperBound(minScore + sumOfOtherMaxScores) > minScoreSum;
|
||||
++iter) {
|
||||
// Important: use ulp of minScoreSum and not minScore to make sure that we
|
||||
// converge quickly.
|
||||
minScore -= Math.ulp(minScoreSum);
|
||||
// this should converge in at most two iterations:
|
||||
// - one because of the subtraction rounding error
|
||||
// - one because of the error introduced by sumUpperBound
|
||||
assert ++iters <= 2 : iters;
|
||||
if (iter > 2) {
|
||||
throw new IllegalStateException(
|
||||
"Could not compute a minimum score for minScore="
|
||||
+ +minScore
|
||||
+ ", minScoreSum="
|
||||
+ minScoreSum
|
||||
+ ", sumOfOtherMaxScores="
|
||||
+ sumOfOtherMaxScores
|
||||
+ ", numClauses="
|
||||
+ numClauses);
|
||||
}
|
||||
}
|
||||
return Math.max(minScore, 0f);
|
||||
}
|
||||
|
|
|
@ -229,4 +229,27 @@ public class TestMaxScoreSumPropagator extends LuceneTestCase {
|
|||
(float) scoreSum <= minCompetitiveScore);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
In https://issues.apache.org/jira/browse/LUCENE-10428 we have observed an issue in production
|
||||
causing an infinite loop in MaxScoreSumPropagator.getMinCompetitiveScore. This is likely
|
||||
caused by calling code that is breaking the assumptions made by MaxScoreSumPropagator,
|
||||
e.g. a query that produces negative or NaN scores. This test reproduces that scenario,
|
||||
and asserts that getMinCompetitiveScore aborts after more than 2 iterations.
|
||||
|
||||
See https://github.com/apache/lucene/pull/711.
|
||||
*/
|
||||
public void testMinCompetitiveScoreIllegalState() throws Exception {
|
||||
List<FakeScorer> scorers = new ArrayList<>();
|
||||
scorers.add(new FakeScorer(-0.16903716f));
|
||||
scorers.add(new FakeScorer(0.62573546f));
|
||||
scorers.add(new FakeScorer(-0.64014715f));
|
||||
float minScoreSum = 0.31314075f;
|
||||
MaxScoreSumPropagator p = new MaxScoreSumPropagator(scorers);
|
||||
Throwable exception =
|
||||
assertThrows(IllegalStateException.class, () -> p.setMinCompetitiveScore(minScoreSum));
|
||||
assertEquals(
|
||||
"Could not compute a minimum score for minScore=1.1223251, minScoreSum=0.31314072, sumOfOtherMaxScores=-0.8091843128204346, numClauses=3",
|
||||
exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue