Skip to content

Commit 4b643c5

Browse files
committed
Account soft deletes in committed segments (#43126)
This change fixes the delete count issue in segment stats where we don't account soft-deleted documents from committed segments. Relates #43103
1 parent c3f1e6a commit 4b643c5

File tree

2 files changed

+66
-4
lines changed

2 files changed

+66
-4
lines changed

server/src/main/java/org/elasticsearch/index/engine/Engine.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -933,7 +933,7 @@ protected void writerSegmentStats(SegmentsStats stats) {
933933
/** How much heap is used that would be freed by a refresh. Note that this may throw {@link AlreadyClosedException}. */
934934
public abstract long getIndexBufferRAMBytesUsed();
935935

936-
protected Segment[] getSegmentInfo(SegmentInfos lastCommittedSegmentInfos, boolean verbose) {
936+
final Segment[] getSegmentInfo(SegmentInfos lastCommittedSegmentInfos, boolean verbose) {
937937
ensureOpen();
938938
Map<String, Segment> segments = new HashMap<>();
939939
// first, go over and compute the search ones...
@@ -960,8 +960,8 @@ protected Segment[] getSegmentInfo(SegmentInfos lastCommittedSegmentInfos, boole
960960
segment = new Segment(info.info.name);
961961
segment.search = false;
962962
segment.committed = true;
963-
segment.docCount = info.info.maxDoc();
964-
segment.delDocCount = info.getDelCount();
963+
segment.delDocCount = info.getDelCount() + info.getSoftDelCount();
964+
segment.docCount = info.info.maxDoc() - segment.delDocCount;
965965
segment.version = info.info.getVersion();
966966
segment.compound = info.info.getUseCompoundFile();
967967
try {

server/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ public void testVersionMapAfterAutoIDDocument() throws IOException {
272272
}
273273
}
274274

275-
public void testSegments() throws Exception {
275+
public void testSegmentsWithoutSoftDeletes() throws Exception {
276276
Settings settings = Settings.builder()
277277
.put(defaultSettings.getSettings())
278278
.put(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), false).build();
@@ -600,6 +600,68 @@ public void testSegmentsStatsIncludingFileSizes() throws Exception {
600600
}
601601
}
602602

603+
public void testSegmentsWithSoftDeletes() throws Exception {
604+
Settings.Builder settings = Settings.builder()
605+
.put(defaultSettings.getSettings())
606+
.put(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), true);
607+
final IndexMetaData indexMetaData = IndexMetaData.builder(defaultSettings.getIndexMetaData()).settings(settings).build();
608+
final IndexSettings indexSettings = IndexSettingsModule.newIndexSettings(indexMetaData);
609+
final AtomicLong globalCheckpoint = new AtomicLong(SequenceNumbers.NO_OPS_PERFORMED);
610+
try (Store store = createStore();
611+
InternalEngine engine = createEngine(config(indexSettings, store, createTempDir(), NoMergePolicy.INSTANCE, null,
612+
null, globalCheckpoint::get))) {
613+
assertThat(engine.segments(false), empty());
614+
int numDocsFirstSegment = randomIntBetween(5, 50);
615+
Set<String> liveDocsFirstSegment = new HashSet<>();
616+
for (int i = 0; i < numDocsFirstSegment; i++) {
617+
String id = Integer.toString(i);
618+
ParsedDocument doc = testParsedDocument(id, null, testDocument(), B_1, null);
619+
engine.index(indexForDoc(doc));
620+
liveDocsFirstSegment.add(id);
621+
}
622+
engine.refresh("test");
623+
List<Segment> segments = engine.segments(randomBoolean());
624+
assertThat(segments, hasSize(1));
625+
assertThat(segments.get(0).getNumDocs(), equalTo(liveDocsFirstSegment.size()));
626+
assertThat(segments.get(0).getDeletedDocs(), equalTo(0));
627+
assertFalse(segments.get(0).committed);
628+
int deletes = 0;
629+
int updates = 0;
630+
int appends = 0;
631+
int iterations = scaledRandomIntBetween(1, 50);
632+
for (int i = 0; i < iterations && liveDocsFirstSegment.isEmpty() == false; i++) {
633+
String idToUpdate = randomFrom(liveDocsFirstSegment);
634+
liveDocsFirstSegment.remove(idToUpdate);
635+
ParsedDocument doc = testParsedDocument(idToUpdate, null, testDocument(), B_1, null);
636+
if (randomBoolean()) {
637+
engine.delete(new Engine.Delete(doc.type(), doc.id(), newUid(doc), primaryTerm.get()));
638+
deletes++;
639+
} else {
640+
engine.index(indexForDoc(doc));
641+
updates++;
642+
}
643+
if (randomBoolean()) {
644+
engine.index(indexForDoc(testParsedDocument(UUIDs.randomBase64UUID(), null, testDocument(), B_1, null)));
645+
appends++;
646+
}
647+
}
648+
boolean committed = randomBoolean();
649+
if (committed) {
650+
engine.flush();
651+
}
652+
engine.refresh("test");
653+
segments = engine.segments(randomBoolean());
654+
assertThat(segments, hasSize(2));
655+
assertThat(segments.get(0).getNumDocs(), equalTo(liveDocsFirstSegment.size()));
656+
assertThat(segments.get(0).getDeletedDocs(), equalTo(updates + deletes));
657+
assertThat(segments.get(0).committed, equalTo(committed));
658+
659+
assertThat(segments.get(1).getNumDocs(), equalTo(updates + appends));
660+
assertThat(segments.get(1).getDeletedDocs(), equalTo(deletes)); // delete tombstones
661+
assertThat(segments.get(1).committed, equalTo(committed));
662+
}
663+
}
664+
603665
public void testCommitStats() throws IOException {
604666
final AtomicLong maxSeqNo = new AtomicLong(SequenceNumbers.NO_OPS_PERFORMED);
605667
final AtomicLong localCheckpoint = new AtomicLong(SequenceNumbers.NO_OPS_PERFORMED);

0 commit comments

Comments
 (0)