diff --git a/docs/changelog/116339.yaml b/docs/changelog/116339.yaml new file mode 100644 index 0000000000000..1767183271812 --- /dev/null +++ b/docs/changelog/116339.yaml @@ -0,0 +1,5 @@ +pr: 116339 +summary: "Index stats enhancement: creation date and `tier_preference`" +area: Stats +type: feature +issues: [] diff --git a/docs/changelog/116867.yaml b/docs/changelog/116867.yaml new file mode 100644 index 0000000000000..7e65055942526 --- /dev/null +++ b/docs/changelog/116867.yaml @@ -0,0 +1,5 @@ +pr: 116867 +summary: "Index stats enhancement: creation date and `tier_preference`" +area: Stats +type: feature +issues: [] diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.stats/16_creation_date_tier_preference.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.stats/16_creation_date_tier_preference.yml new file mode 100644 index 0000000000000..6ecd9c3e9c2ce --- /dev/null +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.stats/16_creation_date_tier_preference.yml @@ -0,0 +1,14 @@ +--- +"Ensure index creation date and tier preference are exposed": + - requires: + cluster_features: ["stats.tier_creation_date"] + reason: index creation date and tier preference added to stats in 8.17 + + - do: + indices.create: + index: myindex + - do: + indices.stats: {} + + - is_true: indices.myindex.creation_date + - is_true: indices.myindex.tier_preference diff --git a/server/src/main/java/module-info.java b/server/src/main/java/module-info.java index 11c3c50502c39..91e30a5365062 100644 --- a/server/src/main/java/module-info.java +++ b/server/src/main/java/module-info.java @@ -420,6 +420,7 @@ provides org.elasticsearch.features.FeatureSpecification with + org.elasticsearch.action.admin.indices.stats.IndicesStatsFeatures, org.elasticsearch.action.bulk.BulkFeatures, org.elasticsearch.features.FeatureInfrastructureFeatures, org.elasticsearch.health.HealthFeatures, diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java index 6aa13508ec467..234f497fde3b5 100644 --- a/server/src/main/java/org/elasticsearch/TransportVersions.java +++ b/server/src/main/java/org/elasticsearch/TransportVersions.java @@ -199,6 +199,7 @@ static TransportVersion def(int id) { public static final TransportVersion VERTEX_AI_INPUT_TYPE_ADDED = def(8_790_00_0); public static final TransportVersion SKIP_INNER_HITS_SEARCH_SOURCE = def(8_791_00_0); public static final TransportVersion QUERY_RULES_LIST_INCLUDES_TYPES = def(8_792_00_0); + public static final TransportVersion INDEX_STATS_ADDITIONAL_FIELDS = def(8_793_00_0); /* * STOP! READ THIS FIRST! No, really, diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/stats/IndexStats.java b/server/src/main/java/org/elasticsearch/action/admin/indices/stats/IndexStats.java index 6106e620521f7..7cefc086e17dc 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/stats/IndexStats.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/stats/IndexStats.java @@ -12,6 +12,7 @@ import org.elasticsearch.cluster.health.ClusterHealthStatus; import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.core.Nullable; +import org.elasticsearch.features.NodeFeature; import java.util.ArrayList; import java.util.HashMap; @@ -21,6 +22,8 @@ public class IndexStats implements Iterable { + public static final NodeFeature TIER_CREATION_DATE = new NodeFeature("stats.tier_creation_date"); + private final String index; private final String uuid; @@ -29,6 +32,10 @@ public class IndexStats implements Iterable { private final IndexMetadata.State state; + private final List tierPreference; + + private final Long creationDate; + private final ShardStats shards[]; public IndexStats( @@ -36,12 +43,16 @@ public IndexStats( String uuid, @Nullable ClusterHealthStatus health, @Nullable IndexMetadata.State state, + @Nullable List tierPreference, + @Nullable Long creationDate, ShardStats[] shards ) { this.index = index; this.uuid = uuid; this.health = health; this.state = state; + this.tierPreference = tierPreference; + this.creationDate = creationDate; this.shards = shards; } @@ -61,6 +72,14 @@ public IndexMetadata.State getState() { return state; } + public List getTierPreference() { + return tierPreference; + } + + public Long getCreationDate() { + return creationDate; + } + public ShardStats[] getShards() { return this.shards; } @@ -129,13 +148,24 @@ public static class IndexStatsBuilder { private final String uuid; private final ClusterHealthStatus health; private final IndexMetadata.State state; + private final List tierPreference; + private final Long creationDate; private final List shards = new ArrayList<>(); - public IndexStatsBuilder(String indexName, String uuid, @Nullable ClusterHealthStatus health, @Nullable IndexMetadata.State state) { + public IndexStatsBuilder( + String indexName, + String uuid, + @Nullable ClusterHealthStatus health, + @Nullable IndexMetadata.State state, + @Nullable List tierPreference, + @Nullable Long creationDate + ) { this.indexName = indexName; this.uuid = uuid; this.health = health; this.state = state; + this.tierPreference = tierPreference; + this.creationDate = creationDate; } public IndexStatsBuilder add(ShardStats shardStats) { @@ -144,7 +174,15 @@ public IndexStatsBuilder add(ShardStats shardStats) { } public IndexStats build() { - return new IndexStats(indexName, uuid, health, state, shards.toArray(new ShardStats[shards.size()])); + return new IndexStats( + indexName, + uuid, + health, + state, + tierPreference, + creationDate, + shards.toArray(new ShardStats[shards.size()]) + ); } } } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsFeatures.java b/server/src/main/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsFeatures.java new file mode 100644 index 0000000000000..2b67885273d05 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsFeatures.java @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +package org.elasticsearch.action.admin.indices.stats; + +import org.elasticsearch.features.FeatureSpecification; +import org.elasticsearch.features.NodeFeature; + +import java.util.Set; + +public class IndicesStatsFeatures implements FeatureSpecification { + + @Override + public Set getFeatures() { + return Set.of(IndexStats.TIER_CREATION_DATE); + } +} diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsResponse.java b/server/src/main/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsResponse.java index 65f7e1969ea69..205f1cbc04e8b 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsResponse.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsResponse.java @@ -47,6 +47,10 @@ public class IndicesStatsResponse extends ChunkedBroadcastResponse { private final Map indexStateMap; + private final Map> indexTierPreferenceMap; + + private final Map indexCreationDateMap; + private final ShardStats[] shards; private Map shardStatsMap; @@ -54,12 +58,23 @@ public class IndicesStatsResponse extends ChunkedBroadcastResponse { IndicesStatsResponse(StreamInput in) throws IOException { super(in); shards = in.readArray(ShardStats::new, ShardStats[]::new); - if (in.getTransportVersion().onOrAfter(TransportVersions.V_8_1_0)) { + if (in.getTransportVersion().onOrAfter(TransportVersions.INDEX_STATS_ADDITIONAL_FIELDS)) { indexHealthMap = in.readMap(ClusterHealthStatus::readFrom); indexStateMap = in.readMap(IndexMetadata.State::readFrom); + indexTierPreferenceMap = in.readMap(StreamInput::readStringCollectionAsList); + indexCreationDateMap = in.readMap(StreamInput::readLong); + } else if (in.getTransportVersion().onOrAfter(TransportVersions.V_8_1_0)) { + // Between 8.1 and INDEX_STATS_ADDITIONAL_FIELDS, we had a different format for the response + // where we only had health and state available. + indexHealthMap = in.readMap(ClusterHealthStatus::readFrom); + indexStateMap = in.readMap(IndexMetadata.State::readFrom); + indexTierPreferenceMap = Map.of(); + indexCreationDateMap = Map.of(); } else { indexHealthMap = Map.of(); indexStateMap = Map.of(); + indexTierPreferenceMap = Map.of(); + indexCreationDateMap = Map.of(); } } @@ -79,6 +94,8 @@ public class IndicesStatsResponse extends ChunkedBroadcastResponse { Objects.requireNonNull(shards); Map indexHealthModifiableMap = new HashMap<>(); Map indexStateModifiableMap = new HashMap<>(); + Map> indexTierPreferenceModifiableMap = new HashMap<>(); + Map indexCreationDateModifiableMap = new HashMap<>(); for (ShardStats shard : shards) { Index index = shard.getShardRouting().index(); IndexMetadata indexMetadata = metadata.index(index); @@ -88,10 +105,14 @@ public class IndicesStatsResponse extends ChunkedBroadcastResponse { ignored -> new ClusterIndexHealth(indexMetadata, routingTable.index(index)).getStatus() ); indexStateModifiableMap.computeIfAbsent(index.getName(), ignored -> indexMetadata.getState()); + indexTierPreferenceModifiableMap.computeIfAbsent(index.getName(), ignored -> indexMetadata.getTierPreference()); + indexCreationDateModifiableMap.computeIfAbsent(index.getName(), ignored -> indexMetadata.getCreationDate()); } } indexHealthMap = unmodifiableMap(indexHealthModifiableMap); indexStateMap = unmodifiableMap(indexStateModifiableMap); + indexTierPreferenceMap = unmodifiableMap(indexTierPreferenceModifiableMap); + indexCreationDateMap = unmodifiableMap(indexCreationDateModifiableMap); } public Map asMap() { @@ -129,7 +150,14 @@ public Map getIndices() { Index index = shard.getShardRouting().index(); IndexStatsBuilder indexStatsBuilder = indexToIndexStatsBuilder.computeIfAbsent( index.getName(), - k -> new IndexStatsBuilder(k, index.getUUID(), indexHealthMap.get(index.getName()), indexStateMap.get(index.getName())) + k -> new IndexStatsBuilder( + k, + index.getUUID(), + indexHealthMap.get(index.getName()), + indexStateMap.get(index.getName()), + indexTierPreferenceMap.get(index.getName()), + indexCreationDateMap.get(index.getName()) + ) ); indexStatsBuilder.add(shard); } @@ -174,7 +202,13 @@ public CommonStats getPrimaries() { public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeArray(shards); - if (out.getTransportVersion().onOrAfter(TransportVersions.V_8_1_0)) { + if (out.getTransportVersion().onOrAfter(TransportVersions.INDEX_STATS_ADDITIONAL_FIELDS)) { + out.writeMap(indexHealthMap, StreamOutput::writeWriteable); + out.writeMap(indexStateMap, StreamOutput::writeWriteable); + out.writeMap(indexTierPreferenceMap, StreamOutput::writeStringCollection); + out.writeMap(indexCreationDateMap, StreamOutput::writeLong); + + } else if (out.getTransportVersion().onOrAfter(TransportVersions.V_8_1_0)) { out.writeMap(indexHealthMap, StreamOutput::writeWriteable); out.writeMap(indexStateMap, StreamOutput::writeWriteable); } @@ -203,6 +237,12 @@ protected Iterator customXContentChunks(ToXContent.Params params) { if (indexStats.getState() != null) { builder.field("status", indexStats.getState().toString().toLowerCase(Locale.ROOT)); } + if (indexStats.getTierPreference() != null) { + builder.field("tier_preference", indexStats.getTierPreference()); + } + if (indexStats.getCreationDate() != null) { + builder.field("creation_date", indexStats.getCreationDate()); + } builder.startObject("primaries"); indexStats.getPrimaries().toXContent(builder, p); builder.endObject(); diff --git a/server/src/main/resources/META-INF/services/org.elasticsearch.features.FeatureSpecification b/server/src/main/resources/META-INF/services/org.elasticsearch.features.FeatureSpecification index 90fc3fe7da834..b858aba15d0fa 100644 --- a/server/src/main/resources/META-INF/services/org.elasticsearch.features.FeatureSpecification +++ b/server/src/main/resources/META-INF/services/org.elasticsearch.features.FeatureSpecification @@ -7,6 +7,7 @@ # License v3.0 only", or the "Server Side Public License, v 1". # +org.elasticsearch.action.admin.indices.stats.IndicesStatsFeatures org.elasticsearch.action.bulk.BulkFeatures org.elasticsearch.features.FeatureInfrastructureFeatures org.elasticsearch.health.HealthFeatures diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/WaitForNoFollowersStepTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/WaitForNoFollowersStepTests.java index 01a12fb795316..21e3155501995 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/WaitForNoFollowersStepTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/WaitForNoFollowersStepTests.java @@ -189,7 +189,10 @@ public void testNoShardStats() { shardStats[0] = sStats; mockXPackInfo(true, true); - mockIndexStatsCall(indexName, new IndexStats(indexName, "uuid", ClusterHealthStatus.GREEN, IndexMetadata.State.OPEN, shardStats)); + mockIndexStatsCall( + indexName, + new IndexStats(indexName, "uuid", ClusterHealthStatus.GREEN, IndexMetadata.State.OPEN, null, null, shardStats) + ); final SetOnce conditionMetHolder = new SetOnce<>(); final SetOnce stepInfoHolder = new SetOnce<>(); @@ -289,7 +292,7 @@ private IndexStats randomIndexStats(boolean isLeaderIndex, int numOfShards) { for (int i = 0; i < numOfShards; i++) { shardStats[i] = randomShardStats(isLeaderIndex); } - return new IndexStats(randomAlphaOfLength(5), randomAlphaOfLength(10), null, null, shardStats); + return new IndexStats(randomAlphaOfLength(5), randomAlphaOfLength(10), null, null, null, null, shardStats); } private ShardStats randomShardStats(boolean isLeaderIndex) { diff --git a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/indices/IndicesStatsMonitoringDocTests.java b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/indices/IndicesStatsMonitoringDocTests.java index 6822f54633bdc..b1d4f3ff7045f 100644 --- a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/indices/IndicesStatsMonitoringDocTests.java +++ b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/indices/IndicesStatsMonitoringDocTests.java @@ -53,6 +53,8 @@ public void setUp() throws Exception { "dcvO5uZATE-EhIKc3tk9Bg", null, null, + null, + null, new ShardStats[] { // Primaries new ShardStats(mockShardRouting(true), mockShardPath(), mockCommonStats(), null, null, null, false, 0),