Skip to content

Commit 976cf4b

Browse files
authored
[7.x] Enhanced segment files sizes information in Nodes Stats/Indices Stats APIs (elastic#71725)
Since elastic#16661 it is possible to know the total sizes for some Lucene segment files by using the Node Stats or Indices Stats API with the include_segment_file_sizes parameter, and the list of file extensions has been extended in elastic#71416. This commit adds a bit more information about file sizes like the number of files (count), the min, max and average file sizes in bytes that share the same extension. Here is a sample: "cfs" : { "description" : "Compound Files", "size_in_bytes" : 2260, "min_size_in_bytes" : 2260, "max_size_in_bytes" : 2260, "average_size_in_bytes" : 2260, "count" : 1 } This commit also simplifies how compound file sizes were computed: before compound segment files were extracted and sizes aggregated with regular non-compound files sizes (which can be confusing and out of the scope of the original issue elastic#6728), now CFS/CFE files appears as distinct files. These new information are provided to give a better view of the segment files and are useful in many cases, specially with frozen searchable snapshots whose segment stats can now be introspected thanks to the include_unloaded_segments parameter. Backport of elastic#71643
1 parent 0a89c1f commit 976cf4b

File tree

9 files changed

+397
-130
lines changed

9 files changed

+397
-130
lines changed

rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.stats/30_segments.yml

+39
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,42 @@ setup:
6161
forbid_closed_indices: false
6262

6363
- match: { indices.test.primaries.segments.count: $num_segments_after_flush }
64+
65+
66+
---
67+
"Indices Stats API with extended files stats":
68+
69+
- skip:
70+
version: " - 7.12.99"
71+
reason: segment files stats extended in 7.13.0
72+
73+
- do:
74+
index:
75+
index: test
76+
id: 1
77+
body: { "foo": "bar" }
78+
79+
- do:
80+
indices.flush:
81+
index: test
82+
83+
- do:
84+
indices.stats:
85+
metric: [ segments ]
86+
include_segment_file_sizes: true
87+
88+
- is_true: _all.total.segments.file_sizes
89+
- is_true: _all.total.segments.file_sizes.si
90+
- gt: { _all.total.segments.file_sizes.si.count: 0 }
91+
- gt: { _all.total.segments.file_sizes.si.size_in_bytes: 0 }
92+
- gt: { _all.total.segments.file_sizes.si.min_size_in_bytes: 0 }
93+
- gt: { _all.total.segments.file_sizes.si.max_size_in_bytes: 0 }
94+
- gt: { _all.total.segments.file_sizes.si.average_size_in_bytes: 0 }
95+
96+
- is_true: indices.test.primaries.segments.file_sizes
97+
- is_true: indices.test.primaries.segments.file_sizes.si
98+
- gt: { indices.test.primaries.segments.file_sizes.si.count: 0 }
99+
- gt: { indices.test.primaries.segments.file_sizes.si.size_in_bytes: 0 }
100+
- gt: { indices.test.primaries.segments.file_sizes.si.min_size_in_bytes: 0 }
101+
- gt: { indices.test.primaries.segments.file_sizes.si.max_size_in_bytes: 0 }
102+
- gt: { indices.test.primaries.segments.file_sizes.si.average_size_in_bytes: 0 }

server/src/internalClusterTest/java/org/elasticsearch/indices/stats/IndexStatsIT.java

+15-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
package org.elasticsearch.indices.stats;
1010

11+
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
1112
import org.apache.lucene.util.LuceneTestCase.SuppressCodecs;
1213
import org.elasticsearch.action.DocWriteResponse;
1314
import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse;
@@ -39,6 +40,7 @@
3940
import org.elasticsearch.index.MergeSchedulerConfig;
4041
import org.elasticsearch.index.VersionType;
4142
import org.elasticsearch.index.cache.query.QueryCacheStats;
43+
import org.elasticsearch.index.engine.SegmentsStats;
4244
import org.elasticsearch.index.engine.VersionConflictEngineException;
4345
import org.elasticsearch.index.query.QueryBuilders;
4446
import org.elasticsearch.index.shard.IndexShard;
@@ -601,11 +603,23 @@ public void testSegmentsStats() {
601603
client().admin().indices().prepareFlush().get();
602604
client().admin().indices().prepareForceMerge().setMaxNumSegments(1).execute().actionGet();
603605
client().admin().indices().prepareRefresh().get();
604-
stats = client().admin().indices().prepareStats().setSegments(true).get();
606+
607+
final boolean includeSegmentFileSizes = randomBoolean();
608+
stats = client().admin().indices().prepareStats().setSegments(true).setIncludeSegmentFileSizes(includeSegmentFileSizes).get();
605609

606610
assertThat(stats.getTotal().getSegments(), notNullValue());
607611
assertThat(stats.getTotal().getSegments().getCount(), equalTo((long) test1.totalNumShards));
608612
assertThat(stats.getTotal().getSegments().getMemoryInBytes(), greaterThan(0L));
613+
if (includeSegmentFileSizes) {
614+
assertThat(stats.getTotal().getSegments().getFiles().size(), greaterThan(0));
615+
for (ObjectObjectCursor<String, SegmentsStats.FileStats> cursor : stats.getTotal().getSegments().getFiles()) {
616+
assertThat(cursor.value.getExt(), notNullValue());
617+
assertThat(cursor.value.getTotal(), greaterThan(0L));
618+
assertThat(cursor.value.getCount(), greaterThan(0L));
619+
assertThat(cursor.value.getMin(), greaterThan(0L));
620+
assertThat(cursor.value.getMax(), greaterThan(0L));
621+
}
622+
}
609623
}
610624

611625
public void testAllFlags() throws Exception {

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

+30-80
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@
2525
import org.apache.lucene.search.ReferenceManager;
2626
import org.apache.lucene.search.similarities.Similarity;
2727
import org.apache.lucene.store.AlreadyClosedException;
28-
import org.apache.lucene.store.Directory;
29-
import org.apache.lucene.store.IOContext;
3028
import org.apache.lucene.util.Accountable;
3129
import org.apache.lucene.util.Accountables;
3230
import org.apache.lucene.util.SetOnce;
@@ -69,10 +67,8 @@
6967
import org.elasticsearch.search.suggest.completion.CompletionStats;
7068

7169
import java.io.Closeable;
72-
import java.io.FileNotFoundException;
7370
import java.io.IOException;
7471
import java.io.UncheckedIOException;
75-
import java.nio.file.NoSuchFileException;
7672
import java.util.Arrays;
7773
import java.util.Base64;
7874
import java.util.Comparator;
@@ -139,7 +135,7 @@ protected Engine(EngineConfig engineConfig) {
139135
this.store = engineConfig.getStore();
140136
// we use the engine class directly here to make sure all subclasses have the same logger name
141137
this.logger = Loggers.getLogger(Engine.class,
142-
engineConfig.getShardId());
138+
engineConfig.getShardId());
143139
this.eventListener = engineConfig.getEventListener();
144140
}
145141

@@ -182,7 +178,7 @@ public DocsStats docStats() {
182178
// when indexing but not refreshing in general. Yet, if a refresh happens the internal searcher is refresh as well so we are
183179
// safe here.
184180
try (Searcher searcher = acquireSearcher("docStats", SearcherScope.INTERNAL)) {
185-
return docsStats(searcher.getIndexReader());
181+
return docsStats(searcher.getIndexReader());
186182
}
187183
}
188184

@@ -289,12 +285,14 @@ boolean throttleLockIsHeldByCurrentThread() { // to be used in assertions and te
289285

290286
/**
291287
* Returns the <code>true</code> iff this engine is currently under index throttling.
288+
*
292289
* @see #getIndexThrottleTimeInMillis()
293290
*/
294291
public abstract boolean isThrottled();
295292

296293
/**
297294
* Trims translog for terms below <code>belowTerm</code> and seq# above <code>aboveSeqNo</code>
295+
*
298296
* @see Translog#trimOperations(long, long)
299297
*/
300298
public abstract void trimOperationsFromTranslog(long belowTerm, long aboveSeqNo) throws EngineException;
@@ -819,86 +817,38 @@ protected void fillSegmentStats(SegmentReader segmentReader, boolean includeSegm
819817
stats.addNormsMemoryInBytes(guardedRamBytesUsed(segmentReader.getNormsReader()));
820818
stats.addPointsMemoryInBytes(guardedRamBytesUsed(segmentReader.getPointsReader()));
821819
stats.addDocValuesMemoryInBytes(guardedRamBytesUsed(segmentReader.getDocValuesReader()));
822-
823820
if (includeSegmentFileSizes) {
824-
// TODO: consider moving this to StoreStats
825-
stats.addFileSizes(getSegmentFileSizes(segmentReader));
821+
stats.addFiles(getSegmentFileSizes(segmentReader));
826822
}
827823
}
828824

829-
private ImmutableOpenMap<String, Long> getSegmentFileSizes(SegmentReader segmentReader) {
830-
Directory directory = null;
831-
SegmentCommitInfo segmentCommitInfo = segmentReader.getSegmentInfo();
832-
boolean useCompoundFile = segmentCommitInfo.info.getUseCompoundFile();
833-
if (useCompoundFile) {
834-
try {
835-
directory = engineConfig.getCodec().compoundFormat().getCompoundReader(segmentReader.directory(),
836-
segmentCommitInfo.info, IOContext.READ);
837-
} catch (IOException e) {
838-
logger.warn(() -> new ParameterizedMessage("Error when opening compound reader for Directory [{}] and " +
839-
"SegmentCommitInfo [{}]", segmentReader.directory(), segmentCommitInfo), e);
840-
841-
return ImmutableOpenMap.of();
842-
}
843-
} else {
844-
directory = segmentReader.directory();
845-
}
846-
847-
assert directory != null;
848-
849-
String[] files;
850-
if (useCompoundFile) {
851-
try {
852-
files = directory.listAll();
853-
} catch (IOException e) {
854-
final Directory finalDirectory = directory;
855-
logger.warn(() ->
856-
new ParameterizedMessage("Couldn't list Compound Reader Directory [{}]", finalDirectory), e);
857-
return ImmutableOpenMap.of();
858-
}
859-
} else {
860-
try {
861-
files = segmentReader.getSegmentInfo().files().toArray(new String[]{});
862-
} catch (IOException e) {
863-
logger.warn(() ->
864-
new ParameterizedMessage("Couldn't list Directory from SegmentReader [{}] and SegmentInfo [{}]",
865-
segmentReader, segmentReader.getSegmentInfo()), e);
866-
return ImmutableOpenMap.of();
867-
}
868-
}
869-
870-
ImmutableOpenMap.Builder<String, Long> map = ImmutableOpenMap.builder();
871-
for (String file : files) {
872-
String extension = IndexFileNames.getExtension(file);
873-
long length = 0L;
874-
try {
875-
length = directory.fileLength(file);
876-
} catch (NoSuchFileException | FileNotFoundException e) {
877-
final Directory finalDirectory = directory;
878-
logger.warn(() -> new ParameterizedMessage("Tried to query fileLength but file is gone [{}] [{}]",
879-
finalDirectory, file), e);
880-
} catch (IOException e) {
881-
final Directory finalDirectory = directory;
882-
logger.warn(() -> new ParameterizedMessage("Error when trying to query fileLength [{}] [{}]",
883-
finalDirectory, file), e);
884-
}
885-
if (length == 0L) {
886-
continue;
887-
}
888-
map.put(extension, length);
889-
}
890-
891-
if (useCompoundFile) {
892-
try {
893-
directory.close();
894-
} catch (IOException e) {
895-
final Directory finalDirectory = directory;
896-
logger.warn(() -> new ParameterizedMessage("Error when closing compound reader on Directory [{}]",
897-
finalDirectory), e);
825+
private ImmutableOpenMap<String, SegmentsStats.FileStats> getSegmentFileSizes(SegmentReader segmentReader) {
826+
try {
827+
final ImmutableOpenMap.Builder<String, SegmentsStats.FileStats> files = ImmutableOpenMap.builder();
828+
final SegmentCommitInfo segmentCommitInfo = segmentReader.getSegmentInfo();
829+
for (String fileName : segmentCommitInfo.files()) {
830+
String fileExtension = IndexFileNames.getExtension(fileName);
831+
if (fileExtension != null) {
832+
try {
833+
long fileLength = segmentReader.directory().fileLength(fileName);
834+
files.put(fileExtension, new SegmentsStats.FileStats(fileExtension, fileLength, 1L, fileLength, fileLength));
835+
} catch (IOException ioe) {
836+
logger.warn(() ->
837+
new ParameterizedMessage("Error when retrieving file length for [{}]", fileName), ioe);
838+
} catch (AlreadyClosedException ace) {
839+
logger.warn(() ->
840+
new ParameterizedMessage("Error when retrieving file length for [{}], directory is closed", fileName), ace);
841+
return ImmutableOpenMap.of();
842+
}
843+
}
898844
}
845+
return files.build();
846+
} catch (IOException e) {
847+
logger.warn(() ->
848+
new ParameterizedMessage("Error when listing files for segment reader [{}] and segment info [{}]",
849+
segmentReader, segmentReader.getSegmentInfo()), e);
850+
return ImmutableOpenMap.of();
899851
}
900-
901-
return map.build();
902852
}
903853

904854
protected void writerSegmentStats(SegmentsStats stats) {

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public SegmentsStats segmentsStats(boolean includeSegmentFileSizes, boolean incl
110110
final SegmentsStats stats = new SegmentsStats();
111111
stats.add(this.segmentsStats);
112112
if (includeSegmentFileSizes == false) {
113-
stats.clearFileSizes();
113+
stats.clearFiles();
114114
}
115115
return stats;
116116
} else {

0 commit comments

Comments
 (0)