Skip to content

Commit

Permalink
Expose index health and status to the _stats API (#81954)
Browse files Browse the repository at this point in the history
Resolves #80413
  • Loading branch information
gmarouli committed Jan 10, 2022
1 parent 0699c93 commit b118d84
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@

package org.elasticsearch.action.admin.indices.stats;

import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.core.Nullable;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
Expand All @@ -20,11 +24,23 @@ public class IndexStats implements Iterable<IndexShardStats> {

private final String uuid;

private final ClusterHealthStatus health;

private final IndexMetadata.State state;

private final ShardStats shards[];

public IndexStats(String index, String uuid, ShardStats[] shards) {
public IndexStats(
String index,
String uuid,
@Nullable ClusterHealthStatus health,
@Nullable IndexMetadata.State state,
ShardStats[] shards
) {
this.index = index;
this.uuid = uuid;
this.health = health;
this.state = state;
this.shards = shards;
}

Expand All @@ -36,6 +52,14 @@ public String getUuid() {
return uuid;
}

public ClusterHealthStatus getHealth() {
return health;
}

public IndexMetadata.State getState() {
return state;
}

public ShardStats[] getShards() {
return this.shards;
}
Expand All @@ -48,11 +72,7 @@ public Map<Integer, IndexShardStats> getIndexShards() {
}
Map<Integer, List<ShardStats>> tmpIndexShards = new HashMap<>();
for (ShardStats shard : shards) {
List<ShardStats> lst = tmpIndexShards.get(shard.getShardRouting().id());
if (lst == null) {
lst = new ArrayList<>();
tmpIndexShards.put(shard.getShardRouting().id(), lst);
}
List<ShardStats> lst = tmpIndexShards.computeIfAbsent(shard.getShardRouting().id(), ignored -> new ArrayList<>());
lst.add(shard);
}
indexShards = new HashMap<>();
Expand Down Expand Up @@ -106,11 +126,15 @@ public CommonStats getPrimaries() {
public static class IndexStatsBuilder {
private final String indexName;
private final String uuid;
private final ClusterHealthStatus health;
private final IndexMetadata.State state;
private final List<ShardStats> shards = new ArrayList<>();

public IndexStatsBuilder(String indexName, String uuid) {
public IndexStatsBuilder(String indexName, String uuid, @Nullable ClusterHealthStatus health, @Nullable IndexMetadata.State state) {
this.indexName = indexName;
this.uuid = uuid;
this.health = health;
this.state = state;
}

public IndexStatsBuilder add(ShardStats shardStats) {
Expand All @@ -119,7 +143,7 @@ public IndexStatsBuilder add(ShardStats shardStats) {
}

public IndexStats build() {
return new IndexStats(indexName, uuid, shards.toArray(new ShardStats[shards.size()]));
return new IndexStats(indexName, uuid, health, state, shards.toArray(new ShardStats[shards.size()]));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@

package org.elasticsearch.action.admin.indices.stats;

import org.elasticsearch.Version;
import org.elasticsearch.action.admin.indices.stats.IndexStats.IndexStatsBuilder;
import org.elasticsearch.action.support.DefaultShardOperationFailedException;
import org.elasticsearch.action.support.broadcast.BroadcastResponse;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.elasticsearch.cluster.health.ClusterIndexHealth;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.cluster.routing.ShardRouting;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
Expand All @@ -21,31 +26,62 @@
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static java.util.Collections.unmodifiableMap;

public class IndicesStatsResponse extends BroadcastResponse {

private ShardStats[] shards;
private final Map<String, ClusterHealthStatus> indexHealthMap;

private final Map<String, IndexMetadata.State> indexStateMap;

private final ShardStats[] shards;

private Map<ShardRouting, ShardStats> shardStatsMap;

IndicesStatsResponse(StreamInput in) throws IOException {
super(in);
shards = in.readArray(ShardStats::new, (size) -> new ShardStats[size]);
shards = in.readArray(ShardStats::new, ShardStats[]::new);
if (in.getVersion().onOrAfter(Version.V_8_1_0)) {
indexHealthMap = in.readMap(StreamInput::readString, ClusterHealthStatus::readFrom);
indexStateMap = in.readMap(StreamInput::readString, IndexMetadata.State::readFrom);
} else {
indexHealthMap = Map.of();
indexStateMap = Map.of();
}
}

IndicesStatsResponse(
ShardStats[] shards,
int totalShards,
int successfulShards,
int failedShards,
List<DefaultShardOperationFailedException> shardFailures
List<DefaultShardOperationFailedException> shardFailures,
ClusterState clusterState
) {
super(totalShards, successfulShards, failedShards, shardFailures);
this.shards = shards;
Objects.requireNonNull(clusterState);
Objects.requireNonNull(shards);
Map<String, ClusterHealthStatus> indexHealthModifiableMap = new HashMap<>();
Map<String, IndexMetadata.State> indexStateModifiableMap = new HashMap<>();
for (ShardStats shard : shards) {
Index index = shard.getShardRouting().index();
IndexMetadata indexMetadata = clusterState.getMetadata().index(index);
if (indexMetadata != null) {
indexHealthModifiableMap.computeIfAbsent(
index.getName(),
ignored -> new ClusterIndexHealth(indexMetadata, clusterState.routingTable().index(index)).getStatus()
);
indexStateModifiableMap.computeIfAbsent(index.getName(), ignored -> indexMetadata.getState());
}
}
indexHealthMap = unmodifiableMap(indexHealthModifiableMap);
indexStateMap = unmodifiableMap(indexStateModifiableMap);
}

public Map<ShardRouting, ShardStats> asMap() {
Expand Down Expand Up @@ -83,7 +119,7 @@ public Map<String, IndexStats> getIndices() {
Index index = shard.getShardRouting().index();
IndexStatsBuilder indexStatsBuilder = indexToIndexStatsBuilder.computeIfAbsent(
index.getName(),
k -> new IndexStatsBuilder(k, index.getUUID())
k -> new IndexStatsBuilder(k, index.getUUID(), indexHealthMap.get(index.getName()), indexStateMap.get(index.getName()))
);
indexStatsBuilder.add(shard);
}
Expand Down Expand Up @@ -128,6 +164,10 @@ public CommonStats getPrimaries() {
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeArray(shards);
if (out.getVersion().onOrAfter(Version.V_8_1_0)) {
out.writeMap(indexHealthMap, StreamOutput::writeString, (o, s) -> s.writeTo(o));
out.writeMap(indexStateMap, StreamOutput::writeString, (o, s) -> s.writeTo(o));
}
}

@Override
Expand Down Expand Up @@ -157,6 +197,12 @@ protected void addCustomXContentFields(XContentBuilder builder, Params params) t
for (IndexStats indexStats : getIndices().values()) {
builder.startObject(indexStats.getIndex());
builder.field("uuid", indexStats.getUuid());
if (indexStats.getHealth() != null) {
builder.field("health", indexStats.getHealth().toString().toLowerCase(Locale.ROOT));
}
if (indexStats.getState() != null) {
builder.field("status", indexStats.getState().toString().toLowerCase(Locale.ROOT));
}
builder.startObject("primaries");
indexStats.getPrimaries().toXContent(builder, params);
builder.endObject();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ protected IndicesStatsResponse newResponse(
totalShards,
successfulShards,
failedShards,
shardFailures
shardFailures,
clusterState
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public class IndexMetadata implements Diffable<IndexMetadata>, ToXContentFragmen
EnumSet.of(ClusterBlockLevel.WRITE)
);

public enum State {
public enum State implements Writeable {
OPEN((byte) 0),
CLOSE((byte) 1);

Expand All @@ -145,6 +145,15 @@ public static State fromId(byte id) {
throw new IllegalStateException("No state match for id [" + id + "]");
}

public static State readFrom(StreamInput in) throws IOException {
byte id = in.readByte();
return switch (id) {
case 0 -> OPEN;
case 1 -> CLOSE;
default -> throw new IllegalStateException("No state match for id [" + id + "]");
};
}

public static State fromString(String state) {
if ("open".equals(state)) {
return OPEN;
Expand All @@ -153,6 +162,11 @@ public static State fromString(String state) {
}
throw new IllegalStateException("No state match for [" + state + "]");
}

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeByte(id);
}
}

static Setting<Integer> buildNumberOfShardsSetting() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,8 @@ public static IndicesStatsResponse randomIndicesStatsResponse(final IndexMetadat
shardStats.size(),
shardStats.size(),
0,
emptyList()
emptyList(),
ClusterState.EMPTY_STATE
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

package org.elasticsearch.action.admin.indices.stats;

import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.routing.ShardRouting;
import org.elasticsearch.cluster.routing.ShardRoutingState;
import org.elasticsearch.cluster.routing.TestShardRouting;
Expand Down Expand Up @@ -35,7 +36,7 @@
public class IndicesStatsResponseTests extends ESTestCase {

public void testInvalidLevel() {
final IndicesStatsResponse response = new IndicesStatsResponse(null, 0, 0, 0, null);
final IndicesStatsResponse response = new IndicesStatsResponse(new ShardStats[0], 0, 0, 0, null, ClusterState.EMPTY_STATE);
final String level = randomAlphaOfLength(16);
final ToXContent.Params params = new ToXContent.MapParams(Collections.singletonMap("level", level));
final IllegalArgumentException e = expectThrows(
Expand Down Expand Up @@ -81,7 +82,8 @@ public void testGetIndices() {
0,
0,
0,
null
null,
ClusterState.EMPTY_STATE
);
Map<String, IndexStats> indexStats = indicesStatsResponse.getIndices();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,14 @@
package org.elasticsearch.action.admin.indices.stats;

import org.elasticsearch.action.ActionFuture;
import org.elasticsearch.action.admin.indices.close.CloseIndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.support.DefaultShardOperationFailedException;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.WriteRequest.RefreshPolicy;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.IndexModule;
Expand All @@ -23,6 +28,7 @@
import org.elasticsearch.xcontent.XContentFactory;

import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
Expand Down Expand Up @@ -143,6 +149,41 @@ public void testUuidOnRootStatsIndices() {
assertEquals(uuid, rsp.getIndex("test").getUuid());
}

public void testIndexHealth() {
String greenIndex = "green-index";
createIndex(greenIndex);
String yellowIndex = "yellow-index";
createIndex(yellowIndex, Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1).build());

IndicesStatsResponse rsp = client().admin().indices().prepareStats().all().get();

IndexStats greenIndexStats = rsp.getIndex(greenIndex);
assertEquals(ClusterHealthStatus.GREEN, greenIndexStats.getHealth());

IndexStats yellowIndexStats = rsp.getIndex(yellowIndex);
assertEquals(ClusterHealthStatus.YELLOW, yellowIndexStats.getHealth());
}

public void testIndexState() throws ExecutionException, InterruptedException {
String openIndex = "open-index";
createIndex(openIndex);
String closeIndex = "close-index";
createIndex(closeIndex);

client().admin().indices().close(new CloseIndexRequest(closeIndex)).get();

IndicesStatsResponse rsp = client().admin()
.indices()
.prepareStats()
.setIndicesOptions(IndicesOptions.STRICT_EXPAND_OPEN_CLOSED)
.get();
IndexStats openIndexStats = rsp.getIndex(openIndex);
assertEquals(IndexMetadata.State.OPEN, openIndexStats.getState());

IndexStats closeIndexStats = rsp.getIndex(closeIndex);
assertEquals(IndexMetadata.State.CLOSE, closeIndexStats.getState());
}

/**
* Gives access to package private IndicesStatsResponse constructor for test purpose.
**/
Expand All @@ -151,8 +192,9 @@ public static IndicesStatsResponse newIndicesStatsResponse(
int totalShards,
int successfulShards,
int failedShards,
List<DefaultShardOperationFailedException> shardFailures
List<DefaultShardOperationFailedException> shardFailures,
ClusterState clusterState
) {
return new IndicesStatsResponse(shards, totalShards, successfulShards, failedShards, shardFailures);
return new IndicesStatsResponse(shards, totalShards, successfulShards, failedShards, shardFailures, clusterState);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.elasticsearch.action.admin.indices.stats.IndicesStatsRequest;
import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
import org.elasticsearch.action.admin.indices.stats.ShardStats;
import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.cluster.metadata.Metadata;
import org.elasticsearch.common.Strings;
Expand Down Expand Up @@ -147,7 +148,7 @@ public void testNoShardStats() {
ShardStats sStats = new ShardStats(null, mockShardPath(), null, null, null, null);
ShardStats[] shardStats = new ShardStats[1];
shardStats[0] = sStats;
mockIndexStatsCall(indexName, new IndexStats(indexName, "uuid", shardStats));
mockIndexStatsCall(indexName, new IndexStats(indexName, "uuid", ClusterHealthStatus.GREEN, IndexMetadata.State.OPEN, shardStats));

final SetOnce<Boolean> conditionMetHolder = new SetOnce<>();
final SetOnce<ToXContentObject> stepInfoHolder = new SetOnce<>();
Expand Down Expand Up @@ -233,7 +234,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), shardStats);
return new IndexStats(randomAlphaOfLength(5), randomAlphaOfLength(10), null, null, shardStats);
}

private ShardStats randomShardStats(boolean isLeaderIndex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ public void setUp() throws Exception {
new IndexStats(
"index-0",
"dcvO5uZATE-EhIKc3tk9Bg",
null,
null,
new ShardStats[] {
// Primaries
new ShardStats(mockShardRouting(true), mockShardPath(), mockCommonStats(), null, null, null),
Expand Down

0 comments on commit b118d84

Please sign in to comment.