Skip to content

Commit 0ebab4a

Browse files
Total data set size in stats (elastic#70625)
With shared cache searchable snapshots we have shards that have a size in S3 that differs from the locally occupied disk space. This commit introduces `store.total_data_set_size` to node and indices stats, allowing to differ between the two. Relates elastic#69820
1 parent 1575ca0 commit 0ebab4a

File tree

16 files changed

+183
-46
lines changed

16 files changed

+183
-46
lines changed

docs/reference/cluster/nodes-stats.asciidoc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,16 @@ Total size of all shards assigned to the node.
256256
(integer)
257257
Total size, in bytes, of all shards assigned to the node.
258258

259+
`total_data_set_size`::
260+
(<<byte-units,byte value>>)
261+
Total data set size of all shards assigned to the node.
262+
This includes the size of shards not stored fully on the node (shared cache searchable snapshots).
263+
264+
`total_data_set_size_in_bytes`::
265+
(integer)
266+
Total data set size, in bytes, of all shards assigned to the node.
267+
This includes the size of shards not stored fully on the node (shared cache searchable snapshots).
268+
259269
`reserved`::
260270
(<<byte-units,byte value>>)
261271
A prediction of how much larger the shard stores on this node will eventually

docs/reference/cluster/stats.asciidoc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,16 @@ Total size of all shards assigned to selected nodes.
240240
(integer)
241241
Total size, in bytes, of all shards assigned to selected nodes.
242242

243+
`total_data_set_size`::
244+
(<<byte-units, byte units>>)
245+
Total data set size of all shards assigned to selected nodes.
246+
This includes the size of shards not stored fully on the nodes (shared cache searchable snapshots).
247+
248+
`total_data_set_size_in_bytes`::
249+
(integer)
250+
Total data set size, in bytes, of all shards assigned to selected nodes.
251+
This includes the size of shards not stored fully on the nodes (shared cache searchable snapshots).
252+
243253
`reserved`::
244254
(<<byte-units,byte value>>)
245255
A prediction of how much larger the shard stores will eventually grow due to
@@ -1238,6 +1248,8 @@ The API returns the following response:
12381248
"store": {
12391249
"size": "16.2kb",
12401250
"size_in_bytes": 16684,
1251+
"total_data_set_size": "16.2kb",
1252+
"total_data_set_size_in_bytes": 16684,
12411253
"reserved": "0b",
12421254
"reserved_in_bytes": 0
12431255
},

rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/nodes.stats/40_store_stats.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,34 @@
44
version: " - 7.99.99"
55
reason: "reserved_in_bytes field is not returned in prior versions"
66
features: [arbitrary_key]
7+
# todo change after backport
8+
version: " - 7.99.99"
9+
reason: "total_data_set_size added in 7.13"
10+
11+
- do:
12+
nodes.info:
13+
node_id: _master
14+
- set:
15+
nodes._arbitrary_key_: master
16+
17+
- do:
18+
nodes.stats:
19+
metric: [ indices ]
20+
index_metric: [ store ]
21+
22+
- is_false: nodes.$master.discovery
23+
- is_true: nodes.$master.indices.store
24+
- gte: { nodes.$master.indices.store.size_in_bytes: 0 }
25+
- gte: { nodes.$master.indices.store.reserved_in_bytes: -1 }
26+
- set:
27+
nodes.$master.indices.store.size_in_bytes: size_in_bytes
28+
- match: { nodes.$master.indices.store.total_data_set_size_in_bytes: $size_in_bytes }
29+
30+
---
31+
#remove when 7.13 is released
32+
"Store stats bwc":
33+
- skip:
34+
features: [arbitrary_key]
735

836
- do:
937
nodes.info:

server/src/main/java/org/elasticsearch/index/IndexService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
import java.util.function.Consumer;
9393
import java.util.function.Function;
9494
import java.util.function.LongSupplier;
95+
import java.util.function.LongUnaryOperator;
9596
import java.util.function.Supplier;
9697

9798
import static java.util.Collections.emptyMap;
@@ -373,7 +374,7 @@ private long getAvgShardSizeInBytes() throws IOException {
373374
long sum = 0;
374375
int count = 0;
375376
for (IndexShard indexShard : this) {
376-
sum += indexShard.store().stats(0L).sizeInBytes();
377+
sum += indexShard.store().stats(0L, LongUnaryOperator.identity()).sizeInBytes();
377378
count++;
378379
}
379380
if (count == 0) {

server/src/main/java/org/elasticsearch/index/shard/IndexShard.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@
180180
import java.util.function.Consumer;
181181
import java.util.function.Function;
182182
import java.util.function.LongSupplier;
183+
import java.util.function.LongUnaryOperator;
183184
import java.util.function.Supplier;
184185
import java.util.stream.Collectors;
185186
import java.util.stream.StreamSupport;
@@ -1085,15 +1086,16 @@ public GetStats getStats() {
10851086
}
10861087

10871088
public StoreStats storeStats() {
1088-
if (DiskThresholdDecider.SETTING_IGNORE_DISK_WATERMARKS.get(indexSettings.getSettings())) {
1089-
// if this shard has no disk footprint then its size is reported as 0
1090-
return new StoreStats(0, 0);
1091-
}
10921089
try {
10931090
final RecoveryState recoveryState = this.recoveryState;
1094-
final long bytesStillToRecover = recoveryState == null ? -1L : recoveryState.getIndex().bytesStillToRecover();
1095-
final long reservedBytes = bytesStillToRecover == -1 ? StoreStats.UNKNOWN_RESERVED_BYTES : bytesStillToRecover;
1096-
return store.stats(reservedBytes);
1091+
if (DiskThresholdDecider.SETTING_IGNORE_DISK_WATERMARKS.get(indexSettings.getSettings())) {
1092+
// if this shard has no disk footprint then its local size is reported as 0
1093+
return store.stats(0, size -> 0);
1094+
} else {
1095+
final long bytesStillToRecover = recoveryState == null ? -1L : recoveryState.getIndex().bytesStillToRecover();
1096+
final long reservedBytes = bytesStillToRecover == -1 ? StoreStats.UNKNOWN_RESERVED_BYTES : bytesStillToRecover;
1097+
return store.stats(reservedBytes, LongUnaryOperator.identity());
1098+
}
10971099
} catch (IOException e) {
10981100
failShard("Failing shard because of exception during storeStats", e);
10991101
throw new ElasticsearchException("io exception while building 'store stats'", e);

server/src/main/java/org/elasticsearch/index/store/Store.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
import java.util.concurrent.atomic.AtomicBoolean;
9292
import java.util.concurrent.locks.ReentrantReadWriteLock;
9393
import java.util.function.Consumer;
94+
import java.util.function.LongUnaryOperator;
9495
import java.util.zip.CRC32;
9596
import java.util.zip.Checksum;
9697

@@ -346,10 +347,12 @@ public CheckIndex.Status checkIndex(PrintStream out) throws IOException {
346347

347348
/**
348349
* @param reservedBytes a prediction of how much larger the store is expected to grow, or {@link StoreStats#UNKNOWN_RESERVED_BYTES}.
350+
* @param localSizeFunction to calculate the local size of the shard based on the shard size.
349351
*/
350-
public StoreStats stats(long reservedBytes) throws IOException {
352+
public StoreStats stats(long reservedBytes, LongUnaryOperator localSizeFunction) throws IOException {
351353
ensureOpen();
352-
return new StoreStats(directory.estimateSize(), reservedBytes);
354+
long sizeInBytes = directory.estimateSize();
355+
return new StoreStats(localSizeFunction.applyAsLong(sizeInBytes), sizeInBytes, reservedBytes);
353356
}
354357

355358
/**

server/src/main/java/org/elasticsearch/index/store/StoreStats.java

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@ public class StoreStats implements Writeable, ToXContentFragment {
2727
public static final long UNKNOWN_RESERVED_BYTES = -1L;
2828

2929
public static final Version RESERVED_BYTES_VERSION = Version.V_7_9_0;
30+
public static final Version TOTAL_DATA_SET_SIZE_SIZE_VERSION = Version.V_7_13_0;
3031

3132
private long sizeInBytes;
33+
private long totalDataSetSizeInBytes;
3234
private long reservedSize;
3335

3436
public StoreStats() {
@@ -40,6 +42,11 @@ public StoreStats(StreamInput in) throws IOException {
4042
if (in.getVersion().before(Version.V_6_0_0_alpha1)) {
4143
in.readVLong(); // throttleTimeInNanos
4244
}
45+
if (in.getVersion().onOrAfter(TOTAL_DATA_SET_SIZE_SIZE_VERSION)) {
46+
totalDataSetSizeInBytes = in.readVLong();
47+
} else {
48+
totalDataSetSizeInBytes = sizeInBytes;
49+
}
4350
if (in.getVersion().onOrAfter(RESERVED_BYTES_VERSION)) {
4451
reservedSize = in.readZLong();
4552
} else {
@@ -49,19 +56,22 @@ public StoreStats(StreamInput in) throws IOException {
4956

5057
/**
5158
* @param sizeInBytes the size of the store in bytes
59+
* @param totalDataSetSizeInBytes the size of the total data set in bytes, can differ from sizeInBytes for shards using shared cache
60+
* storage
5261
* @param reservedSize a prediction of how much larger the store is expected to grow, or {@link StoreStats#UNKNOWN_RESERVED_BYTES}.
5362
*/
54-
public StoreStats(long sizeInBytes, long reservedSize) {
63+
public StoreStats(long sizeInBytes, long totalDataSetSizeInBytes, long reservedSize) {
5564
assert reservedSize == UNKNOWN_RESERVED_BYTES || reservedSize >= 0 : reservedSize;
5665
this.sizeInBytes = sizeInBytes;
66+
this.totalDataSetSizeInBytes = totalDataSetSizeInBytes;
5767
this.reservedSize = reservedSize;
5868
}
59-
6069
public void add(StoreStats stats) {
6170
if (stats == null) {
6271
return;
6372
}
6473
sizeInBytes += stats.sizeInBytes;
74+
totalDataSetSizeInBytes += stats.totalDataSetSizeInBytes;
6575
reservedSize = ignoreIfUnknown(reservedSize) + ignoreIfUnknown(stats.reservedSize);
6676
}
6777

@@ -85,6 +95,14 @@ public ByteSizeValue getSize() {
8595
return size();
8696
}
8797

98+
public ByteSizeValue totalDataSetSize() {
99+
return new ByteSizeValue(totalDataSetSizeInBytes);
100+
}
101+
102+
public ByteSizeValue getTotalDataSetSize() {
103+
return totalDataSetSize();
104+
}
105+
88106
/**
89107
* A prediction of how much larger this store will eventually grow. For instance, if we are currently doing a peer recovery or restoring
90108
* a snapshot into this store then we can account for the rest of the recovery using this field. A value of {@code -1B} indicates that
@@ -100,6 +118,9 @@ public void writeTo(StreamOutput out) throws IOException {
100118
if (out.getVersion().before(Version.V_6_0_0_alpha1)) {
101119
out.writeVLong(0L); // throttleTimeInNanos
102120
}
121+
if (out.getVersion().onOrAfter(TOTAL_DATA_SET_SIZE_SIZE_VERSION)) {
122+
out.writeVLong(totalDataSetSizeInBytes);
123+
}
103124
if (out.getVersion().onOrAfter(RESERVED_BYTES_VERSION)) {
104125
out.writeZLong(reservedSize);
105126
}
@@ -109,6 +130,7 @@ public void writeTo(StreamOutput out) throws IOException {
109130
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
110131
builder.startObject(Fields.STORE);
111132
builder.humanReadableField(Fields.SIZE_IN_BYTES, Fields.SIZE, size());
133+
builder.humanReadableField(Fields.TOTAL_DATA_SET_SIZE_IN_BYTES, Fields.TOTAL_DATA_SET_SIZE, totalDataSetSize());
112134
builder.humanReadableField(Fields.RESERVED_IN_BYTES, Fields.RESERVED, getReservedSize());
113135
builder.endObject();
114136
return builder;
@@ -118,6 +140,8 @@ static final class Fields {
118140
static final String STORE = "store";
119141
static final String SIZE = "size";
120142
static final String SIZE_IN_BYTES = "size_in_bytes";
143+
static final String TOTAL_DATA_SET_SIZE = "total_data_set_size";
144+
static final String TOTAL_DATA_SET_SIZE_IN_BYTES = "total_data_set_size_in_bytes";
121145
static final String RESERVED = "reserved";
122146
static final String RESERVED_IN_BYTES = "reserved_in_bytes";
123147
}

server/src/test/java/org/elasticsearch/action/admin/cluster/stats/VersionStatsTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public void testCreation() {
9999
Path path = createTempDir().resolve("indices").resolve(shardRouting.shardId().getIndex().getUUID())
100100
.resolve(String.valueOf(shardRouting.shardId().id()));
101101
IndexShard indexShard = mock(IndexShard.class);
102-
StoreStats storeStats = new StoreStats(100, 200);
102+
StoreStats storeStats = new StoreStats(100, 150, 200);
103103
when(indexShard.storeStats()).thenReturn(storeStats);
104104
ShardStats shardStats = new ShardStats(shardRouting, new ShardPath(false, path, path, shardRouting.shardId()),
105105
new CommonStats(null, indexShard, new CommonStatsFlags(CommonStatsFlags.Flag.Store)),

server/src/test/java/org/elasticsearch/action/admin/indices/shrink/TransportResizeActionTests.java

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ public void testErrorCondition() {
7070
assertTrue(
7171
expectThrows(IllegalStateException.class, () ->
7272
TransportResizeAction.prepareCreateIndexRequest(new ResizeRequest("target", "source"), state,
73-
new StoreStats(between(1, 100), between(1, 100)), (i) -> new DocsStats(Integer.MAX_VALUE, between(1, 1000),
73+
new StoreStats(between(1, 100), between(0, 100), between(1, 100)),
74+
(i) -> new DocsStats(Integer.MAX_VALUE, between(1, 1000),
7475
between(1, 100)), "target")
7576
).getMessage().startsWith("Can't merge index with more than [2147483519] docs - too many documents in shards "));
7677

@@ -82,7 +83,7 @@ public void testErrorCondition() {
8283
TransportResizeAction.prepareCreateIndexRequest(req,
8384
createClusterState("source", 8, 1,
8485
Settings.builder().put("index.blocks.write", true).build()).metadata().index("source"),
85-
new StoreStats(between(1, 100), between(1, 100)),
86+
new StoreStats(between(1, 100), between(0, 100), between(1, 100)),
8687
(i) -> i == 2 || i == 3 ? new DocsStats(Integer.MAX_VALUE / 2, between(1, 1000), between(1, 10000)) : null
8788
, "target");
8889
}
@@ -96,7 +97,7 @@ public void testErrorCondition() {
9697
createClusterState("source", 8, 1,
9798
Settings.builder().put("index.blocks.write", true).put("index.soft_deletes.enabled", true).build())
9899
.metadata().index("source"),
99-
new StoreStats(between(1, 100), between(1, 100)),
100+
new StoreStats(between(1, 100), between(0, 100), between(1, 100)),
100101
(i) -> new DocsStats(between(10, 1000), between(1, 10), between(1, 10000)), "target");
101102
});
102103
assertThat(softDeletesError.getMessage(), equalTo("Can't disable [index.soft_deletes.enabled] setting on resize"));
@@ -117,7 +118,7 @@ public void testErrorCondition() {
117118
clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();
118119

119120
TransportResizeAction.prepareCreateIndexRequest(new ResizeRequest("target", "source"), clusterState.metadata().index("source"),
120-
new StoreStats(between(1, 100), between(1, 100)),
121+
new StoreStats(between(1, 100), between(0, 100), between(1, 100)),
121122
(i) -> new DocsStats(between(1, 1000), between(1, 1000), between(0, 10000)), "target");
122123
}
123124

@@ -141,15 +142,17 @@ public void testPassNumRoutingShards() {
141142
resizeRequest.getTargetIndexRequest()
142143
.settings(Settings.builder().put("index.number_of_shards", 2).build());
143144
IndexMetadata indexMetadata = clusterState.metadata().index("source");
144-
TransportResizeAction.prepareCreateIndexRequest(resizeRequest, indexMetadata, new StoreStats(between(1, 100), between(1, 100)),
145+
TransportResizeAction.prepareCreateIndexRequest(resizeRequest, indexMetadata,
146+
new StoreStats(between(1, 100), between(0, 100), between(1, 100)),
145147
null, "target");
146148

147149
resizeRequest.getTargetIndexRequest()
148150
.settings(Settings.builder()
149151
.put("index.number_of_routing_shards", randomIntBetween(2, 10))
150152
.put("index.number_of_shards", 2)
151153
.build());
152-
TransportResizeAction.prepareCreateIndexRequest(resizeRequest, indexMetadata, new StoreStats(between(1, 100), between(1, 100)),
154+
TransportResizeAction.prepareCreateIndexRequest(resizeRequest, indexMetadata,
155+
new StoreStats(between(1, 100), between(0, 100), between(1, 100)),
153156
null, "target");
154157
}
155158

@@ -174,7 +177,7 @@ public void testPassNumRoutingShardsAndFail() {
174177
resizeRequest.getTargetIndexRequest()
175178
.settings(Settings.builder().put("index.number_of_shards", numShards * 2).build());
176179
TransportResizeAction.prepareCreateIndexRequest(resizeRequest, clusterState.metadata().index("source"),
177-
new StoreStats(between(1, 100), between(1, 100)), null, "target");
180+
new StoreStats(between(1, 100), between(0, 100), between(1, 100)), null, "target");
178181

179182
resizeRequest.getTargetIndexRequest()
180183
.settings(Settings.builder()
@@ -183,7 +186,7 @@ public void testPassNumRoutingShardsAndFail() {
183186
ClusterState finalState = clusterState;
184187
IllegalArgumentException iae = expectThrows(IllegalArgumentException.class,
185188
() -> TransportResizeAction.prepareCreateIndexRequest(resizeRequest, finalState.metadata().index("source"),
186-
new StoreStats(between(1, 100), between(1, 100)), null, "target"));
189+
new StoreStats(between(1, 100), between(0, 100), between(1, 100)), null, "target"));
187190
assertEquals("cannot provide index.number_of_routing_shards on resize", iae.getMessage());
188191
}
189192

@@ -211,7 +214,8 @@ public void testShrinkIndexSettings() {
211214
final ActiveShardCount activeShardCount = randomBoolean() ? ActiveShardCount.ALL : ActiveShardCount.ONE;
212215
target.setWaitForActiveShards(activeShardCount);
213216
CreateIndexClusterStateUpdateRequest request = TransportResizeAction.prepareCreateIndexRequest(
214-
target, clusterState.metadata().index(indexName), new StoreStats(between(1, 100), between(1, 100)), (i) -> stats, "target");
217+
target, clusterState.metadata().index(indexName),
218+
new StoreStats(between(1, 100), between(0, 100), between(1, 100)), (i) -> stats, "target");
215219
assertNotNull(request.recoverFrom());
216220
assertEquals(indexName, request.recoverFrom().getName());
217221
assertEquals("1", request.settings().get("index.number_of_shards"));
@@ -242,7 +246,8 @@ public void testShrinkWithMaxPrimaryShardSize() {
242246
.settings(Settings.builder().put("index.number_of_shards", 2).build());
243247
assertTrue(
244248
expectThrows(IllegalArgumentException.class, () ->
245-
TransportResizeAction.prepareCreateIndexRequest(resizeRequest, state, new StoreStats(between(1, 100), between(1, 100)),
249+
TransportResizeAction.prepareCreateIndexRequest(resizeRequest, state,
250+
new StoreStats(between(1, 100), between(0, 100), between(1, 100)),
246251
(i) -> new DocsStats(Integer.MAX_VALUE, between(1, 1000), between(1, 100)), "target")
247252
).getMessage().startsWith("Cannot set both index.number_of_shards and max_primary_shard_size for the target index"));
248253

@@ -268,7 +273,7 @@ public void testShrinkWithMaxPrimaryShardSize() {
268273
// each shard's storage will not be greater than the `max_primary_shard_size`
269274
ResizeRequest target1 = new ResizeRequest("target", "source");
270275
target1.setMaxPrimaryShardSize(new ByteSizeValue(2));
271-
StoreStats storeStats = new StoreStats(10, between(1, 100));
276+
StoreStats storeStats = new StoreStats(10, between(0, 100), between(1, 100));
272277
final int targetIndexShardsNum1 = 5;
273278
final ActiveShardCount activeShardCount1 = ActiveShardCount.from(targetIndexShardsNum1);
274279
target1.setWaitForActiveShards(targetIndexShardsNum1);
@@ -285,7 +290,7 @@ public void testShrinkWithMaxPrimaryShardSize() {
285290
// the shards number of the target index will be equal to the source index's shards number
286291
ResizeRequest target2 = new ResizeRequest("target2", "source");
287292
target2.setMaxPrimaryShardSize(new ByteSizeValue(1));
288-
StoreStats storeStats2 = new StoreStats(100, between(1, 100));
293+
StoreStats storeStats2 = new StoreStats(100, between(0, 100), between(1, 100));
289294
final int targetIndexShardsNum2 = 10;
290295
final ActiveShardCount activeShardCount2 = ActiveShardCount.from(targetIndexShardsNum2);
291296
target2.setWaitForActiveShards(activeShardCount2);

0 commit comments

Comments
 (0)