in lucene/core/src/java/org/apache/lucene/index/TieredMergePolicy.java [748:925]
public MergeSpecification findForcedMerges(
SegmentInfos infos,
int maxSegmentCount,
Map<SegmentCommitInfo, Boolean> segmentsToMerge,
MergeContext mergeContext)
throws IOException {
if (verbose(mergeContext)) {
message(
"findForcedMerges maxSegmentCount="
+ maxSegmentCount
+ " infos="
+ segString(mergeContext, infos)
+ " segmentsToMerge="
+ segmentsToMerge,
mergeContext);
}
List<SegmentSizeAndDocs> sortedSizeAndDocs = getSortedBySegmentSize(infos, mergeContext);
long totalMergeBytes = 0;
final Set<SegmentCommitInfo> merging = mergeContext.getMergingSegments();
// Trim the list down, remove if we're respecting max segment size and it's not original.
// Presumably it's been merged before and is close enough to the max segment size we
// shouldn't add it in again.
Iterator<SegmentSizeAndDocs> iter = sortedSizeAndDocs.iterator();
boolean forceMergeRunning = false;
while (iter.hasNext()) {
SegmentSizeAndDocs segSizeDocs = iter.next();
final Boolean isOriginal = segmentsToMerge.get(segSizeDocs.segInfo);
if (isOriginal == null) {
iter.remove();
} else {
if (merging.contains(segSizeDocs.segInfo)) {
forceMergeRunning = true;
iter.remove();
} else {
totalMergeBytes += segSizeDocs.sizeInBytes;
}
}
}
long maxMergeBytes = maxMergedSegmentBytes;
// Set the maximum segment size based on how many segments have been specified.
if (maxSegmentCount == 1) {
maxMergeBytes = Long.MAX_VALUE;
} else if (maxSegmentCount != Integer.MAX_VALUE) {
maxMergeBytes =
Math.max(
(long) (((double) totalMergeBytes / (double) maxSegmentCount)),
maxMergedSegmentBytes);
// Fudge this up a bit so we have a better chance of not having to do a second pass of merging
// to get
// down to the requested target segment count. If we use the exact size, it's almost
// guaranteed
// that the segments selected below won't fit perfectly and we'll be left with more segments
// than
// we want and have to re-merge in the code at the bottom of this method.
maxMergeBytes = (long) ((double) maxMergeBytes * 1.25);
}
iter = sortedSizeAndDocs.iterator();
boolean foundDeletes = false;
while (iter.hasNext()) {
SegmentSizeAndDocs segSizeDocs = iter.next();
Boolean isOriginal = segmentsToMerge.get(segSizeDocs.segInfo);
if (segSizeDocs.delCount != 0) {
// This is forceMerge; all segments with deleted docs should be merged.
if (isOriginal != null && isOriginal) {
foundDeletes = true;
}
continue;
}
// Let the scoring handle whether to merge large segments.
if (maxSegmentCount == Integer.MAX_VALUE && isOriginal != null && isOriginal == false) {
iter.remove();
}
// Don't try to merge a segment with no deleted docs that's over the max size.
if (maxSegmentCount != Integer.MAX_VALUE && segSizeDocs.sizeInBytes >= maxMergeBytes) {
iter.remove();
}
}
// Nothing to merge this round.
if (sortedSizeAndDocs.size() == 0) {
return null;
}
// We only bail if there are no deletions
if (foundDeletes == false) {
SegmentCommitInfo infoZero = sortedSizeAndDocs.get(0).segInfo;
if ((maxSegmentCount != Integer.MAX_VALUE
&& maxSegmentCount > 1
&& sortedSizeAndDocs.size() <= maxSegmentCount)
|| (maxSegmentCount == 1
&& sortedSizeAndDocs.size() == 1
&& (segmentsToMerge.get(infoZero) != null
|| isMerged(infos, infoZero, mergeContext)))) {
if (verbose(mergeContext)) {
message("already merged", mergeContext);
}
return null;
}
}
if (verbose(mergeContext)) {
message("eligible=" + sortedSizeAndDocs, mergeContext);
}
final int startingSegmentCount = sortedSizeAndDocs.size();
if (forceMergeRunning) {
// hmm this is a little dangerous -- if a user kicks off a forceMerge, it is taking forever,
// lots of
// new indexing/segments happened since, and they want to kick off another to ensure those
// newly
// indexed segments partake in the force merge, they (silently) won't due to this?
return null;
}
// This is the special case of merging down to one segment
if (maxSegmentCount == 1 && totalMergeBytes < maxMergeBytes) {
MergeSpecification spec = new MergeSpecification();
List<SegmentCommitInfo> allOfThem = new ArrayList<>();
for (SegmentSizeAndDocs segSizeDocs : sortedSizeAndDocs) {
allOfThem.add(segSizeDocs.segInfo);
}
spec.add(new OneMerge(allOfThem));
return spec;
}
MergeSpecification spec = null;
int index = startingSegmentCount - 1;
int resultingSegments = startingSegmentCount;
while (true) {
List<SegmentCommitInfo> candidate = new ArrayList<>();
long currentCandidateBytes = 0L;
while (index >= 0 && resultingSegments > maxSegmentCount) {
final SegmentCommitInfo current = sortedSizeAndDocs.get(index).segInfo;
final int initialCandidateSize = candidate.size();
final long currentSegmentSize = current.sizeInBytes();
// We either add to the bin because there's space or because the it is the smallest possible
// bin since
// decrementing the index will move us to even larger segments.
if (currentCandidateBytes + currentSegmentSize <= maxMergeBytes
|| initialCandidateSize < 2) {
candidate.add(current);
--index;
currentCandidateBytes += currentSegmentSize;
if (initialCandidateSize > 0) {
// Any merge that handles two or more segments reduces the resulting number of segments
// by the number of segments handled - 1
--resultingSegments;
}
} else {
break;
}
}
final int candidateSize = candidate.size();
// While a force merge is running, only merges that cover the maximum allowed number of
// segments or that create a segment close to the
// maximum allowed segment sized are permitted
if (candidateSize > 1
&& (forceMergeRunning == false || currentCandidateBytes > 0.7 * maxMergeBytes)) {
final OneMerge merge = new OneMerge(candidate);
if (verbose(mergeContext)) {
message("add merge=" + segString(mergeContext, merge.segments), mergeContext);
}
if (spec == null) {
spec = new MergeSpecification();
}
spec.add(merge);
} else {
return spec;
}
}
}