Remove validation errors from cluster health response
Cluster health responses have not shown validation errors, which are retrieved from RoutingTable validations, in any production or testing instances. The code is unit tested well in this area and any issues are exposed through the testing infrastructure, so this commit removes reporting of validation errors in the cluster health response. Closes #17773 Closes #16979
This commit is contained in:
parent
81449fc912
commit
4613d3bcf9
|
@ -596,8 +596,7 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
|
|||
org.elasticsearch.common.util.concurrent.EsRejectedExecutionException::new, 59),
|
||||
EARLY_TERMINATION_EXCEPTION(org.elasticsearch.common.lucene.Lucene.EarlyTerminationException.class,
|
||||
org.elasticsearch.common.lucene.Lucene.EarlyTerminationException::new, 60),
|
||||
ROUTING_VALIDATION_EXCEPTION(org.elasticsearch.cluster.routing.RoutingValidationException.class,
|
||||
org.elasticsearch.cluster.routing.RoutingValidationException::new, 61),
|
||||
// 61 used to be for RoutingValidationException
|
||||
NOT_SERIALIZABLE_EXCEPTION_WRAPPER(org.elasticsearch.common.io.stream.NotSerializableExceptionWrapper.class,
|
||||
org.elasticsearch.common.io.stream.NotSerializableExceptionWrapper::new, 62),
|
||||
ALIAS_FILTER_PARSING_EXCEPTION(org.elasticsearch.indices.AliasFilterParsingException.class,
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
package org.elasticsearch.action.admin.cluster.health;
|
||||
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.action.ActionResponse;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.health.ClusterHealthStatus;
|
||||
|
@ -34,7 +33,6 @@ import org.elasticsearch.common.xcontent.XContentFactory;
|
|||
import org.elasticsearch.rest.RestStatus;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -82,14 +80,6 @@ public class ClusterHealthResponse extends ActionResponse implements StatusToXCo
|
|||
return clusterStateHealth;
|
||||
}
|
||||
|
||||
/**
|
||||
* The validation failures on the cluster level (without index validation failures).
|
||||
*/
|
||||
public List<String> getValidationFailures() {
|
||||
return clusterStateHealth.getValidationFailures();
|
||||
}
|
||||
|
||||
|
||||
public int getActiveShards() {
|
||||
return clusterStateHealth.getActiveShards();
|
||||
}
|
||||
|
@ -250,7 +240,6 @@ public class ClusterHealthResponse extends ActionResponse implements StatusToXCo
|
|||
static final String RELOCATING_SHARDS = "relocating_shards";
|
||||
static final String INITIALIZING_SHARDS = "initializing_shards";
|
||||
static final String UNASSIGNED_SHARDS = "unassigned_shards";
|
||||
static final String VALIDATION_FAILURES = "validation_failures";
|
||||
static final String INDICES = "indices";
|
||||
}
|
||||
|
||||
|
@ -275,32 +264,6 @@ public class ClusterHealthResponse extends ActionResponse implements StatusToXCo
|
|||
String level = params.param("level", "cluster");
|
||||
boolean outputIndices = "indices".equals(level) || "shards".equals(level);
|
||||
|
||||
|
||||
if (!getValidationFailures().isEmpty()) {
|
||||
builder.startArray(Fields.VALIDATION_FAILURES);
|
||||
for (String validationFailure : getValidationFailures()) {
|
||||
builder.value(validationFailure);
|
||||
}
|
||||
// if we don't print index level information, still print the index validation failures
|
||||
// so we know why the status is red
|
||||
if (!outputIndices) {
|
||||
for (ClusterIndexHealth indexHealth : clusterStateHealth.getIndices().values()) {
|
||||
builder.startObject(indexHealth.getIndex());
|
||||
|
||||
if (!indexHealth.getValidationFailures().isEmpty()) {
|
||||
builder.startArray(Fields.VALIDATION_FAILURES);
|
||||
for (String validationFailure : indexHealth.getValidationFailures()) {
|
||||
builder.value(validationFailure);
|
||||
}
|
||||
builder.endArray();
|
||||
}
|
||||
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
||||
builder.endArray();
|
||||
}
|
||||
|
||||
if (outputIndices) {
|
||||
builder.startObject(Fields.INDICES);
|
||||
for (ClusterIndexHealth indexHealth : clusterStateHealth.getIndices().values()) {
|
||||
|
|
|
@ -29,10 +29,8 @@ import org.elasticsearch.common.xcontent.ToXContent;
|
|||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -60,8 +58,6 @@ public final class ClusterIndexHealth implements Iterable<ClusterShardHealth>, S
|
|||
|
||||
private final Map<Integer, ClusterShardHealth> shards = new HashMap<>();
|
||||
|
||||
private List<String> validationFailures;
|
||||
|
||||
private ClusterIndexHealth() {
|
||||
}
|
||||
|
||||
|
@ -69,7 +65,6 @@ public final class ClusterIndexHealth implements Iterable<ClusterShardHealth>, S
|
|||
this.index = indexMetaData.getIndex().getName();
|
||||
this.numberOfShards = indexMetaData.getNumberOfShards();
|
||||
this.numberOfReplicas = indexMetaData.getNumberOfReplicas();
|
||||
this.validationFailures = indexRoutingTable.validate(indexMetaData);
|
||||
|
||||
for (IndexShardRoutingTable shardRoutingTable : indexRoutingTable) {
|
||||
int shardId = shardRoutingTable.shardId().id();
|
||||
|
@ -95,9 +90,7 @@ public final class ClusterIndexHealth implements Iterable<ClusterShardHealth>, S
|
|||
status = ClusterHealthStatus.YELLOW;
|
||||
}
|
||||
}
|
||||
if (!validationFailures.isEmpty()) {
|
||||
status = ClusterHealthStatus.RED;
|
||||
} else if (shards.isEmpty()) { // might be since none has been created yet (two phase index creation)
|
||||
if (shards.isEmpty()) { // might be since none has been created yet (two phase index creation)
|
||||
status = ClusterHealthStatus.RED;
|
||||
}
|
||||
}
|
||||
|
@ -106,10 +99,6 @@ public final class ClusterIndexHealth implements Iterable<ClusterShardHealth>, S
|
|||
return index;
|
||||
}
|
||||
|
||||
public List<String> getValidationFailures() {
|
||||
return this.validationFailures;
|
||||
}
|
||||
|
||||
public int getNumberOfShards() {
|
||||
return numberOfShards;
|
||||
}
|
||||
|
@ -174,7 +163,6 @@ public final class ClusterIndexHealth implements Iterable<ClusterShardHealth>, S
|
|||
ClusterShardHealth shardHealth = readClusterShardHealth(in);
|
||||
shards.put(shardHealth.getId(), shardHealth);
|
||||
}
|
||||
validationFailures = Arrays.asList(in.readStringArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -193,11 +181,6 @@ public final class ClusterIndexHealth implements Iterable<ClusterShardHealth>, S
|
|||
for (ClusterShardHealth shardHealth : this) {
|
||||
shardHealth.writeTo(out);
|
||||
}
|
||||
|
||||
out.writeVInt(validationFailures.size());
|
||||
for (String failure : validationFailures) {
|
||||
out.writeString(failure);
|
||||
}
|
||||
}
|
||||
|
||||
static final class Fields {
|
||||
|
@ -209,7 +192,6 @@ public final class ClusterIndexHealth implements Iterable<ClusterShardHealth>, S
|
|||
static final String RELOCATING_SHARDS = "relocating_shards";
|
||||
static final String INITIALIZING_SHARDS = "initializing_shards";
|
||||
static final String UNASSIGNED_SHARDS = "unassigned_shards";
|
||||
static final String VALIDATION_FAILURES = "validation_failures";
|
||||
static final String SHARDS = "shards";
|
||||
static final String PRIMARY_ACTIVE = "primary_active";
|
||||
}
|
||||
|
@ -225,14 +207,6 @@ public final class ClusterIndexHealth implements Iterable<ClusterShardHealth>, S
|
|||
builder.field(Fields.INITIALIZING_SHARDS, getInitializingShards());
|
||||
builder.field(Fields.UNASSIGNED_SHARDS, getUnassignedShards());
|
||||
|
||||
if (!getValidationFailures().isEmpty()) {
|
||||
builder.startArray(Fields.VALIDATION_FAILURES);
|
||||
for (String validationFailure : getValidationFailures()) {
|
||||
builder.value(validationFailure);
|
||||
}
|
||||
builder.endArray();
|
||||
}
|
||||
|
||||
if ("shards".equals(params.param("level", "indices"))) {
|
||||
builder.startObject(Fields.SHARDS);
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ import org.elasticsearch.cluster.metadata.IndexMetaData;
|
|||
import org.elasticsearch.cluster.metadata.MetaData;
|
||||
import org.elasticsearch.cluster.routing.IndexRoutingTable;
|
||||
import org.elasticsearch.cluster.routing.RoutingTable;
|
||||
import org.elasticsearch.cluster.routing.RoutingTableValidation;
|
||||
import org.elasticsearch.cluster.routing.ShardRouting;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
|
@ -50,7 +49,6 @@ public final class ClusterStateHealth implements Iterable<ClusterIndexHealth>, S
|
|||
private int unassignedShards = 0;
|
||||
private double activeShardsPercent = 100;
|
||||
private ClusterHealthStatus status = ClusterHealthStatus.RED;
|
||||
private List<String> validationFailures;
|
||||
private Map<String, ClusterIndexHealth> indices = new HashMap<>();
|
||||
|
||||
public static ClusterStateHealth readClusterHealth(StreamInput in) throws IOException {
|
||||
|
@ -89,8 +87,6 @@ public final class ClusterStateHealth implements Iterable<ClusterIndexHealth>, S
|
|||
* @param concreteIndices An array of index names to consider. Must not be null but may be empty.
|
||||
*/
|
||||
public ClusterStateHealth(ClusterState clusterState, String[] concreteIndices) {
|
||||
RoutingTableValidation validation = clusterState.routingTable().validate(clusterState.metaData());
|
||||
validationFailures = validation.failures();
|
||||
numberOfNodes = clusterState.nodes().getSize();
|
||||
numberOfDataNodes = clusterState.nodes().getDataNodes().size();
|
||||
|
||||
|
@ -121,9 +117,7 @@ public final class ClusterStateHealth implements Iterable<ClusterIndexHealth>, S
|
|||
}
|
||||
}
|
||||
|
||||
if (!validationFailures.isEmpty()) {
|
||||
status = ClusterHealthStatus.RED;
|
||||
} else if (clusterState.blocks().hasGlobalBlock(RestStatus.SERVICE_UNAVAILABLE)) {
|
||||
if (clusterState.blocks().hasGlobalBlock(RestStatus.SERVICE_UNAVAILABLE)) {
|
||||
status = ClusterHealthStatus.RED;
|
||||
}
|
||||
|
||||
|
@ -142,10 +136,6 @@ public final class ClusterStateHealth implements Iterable<ClusterIndexHealth>, S
|
|||
}
|
||||
}
|
||||
|
||||
public List<String> getValidationFailures() {
|
||||
return Collections.unmodifiableList(validationFailures);
|
||||
}
|
||||
|
||||
public int getActiveShards() {
|
||||
return activeShards;
|
||||
}
|
||||
|
@ -206,14 +196,6 @@ public final class ClusterStateHealth implements Iterable<ClusterIndexHealth>, S
|
|||
ClusterIndexHealth indexHealth = readClusterIndexHealth(in);
|
||||
indices.put(indexHealth.getIndex(), indexHealth);
|
||||
}
|
||||
size = in.readVInt();
|
||||
if (size == 0) {
|
||||
validationFailures = Collections.emptyList();
|
||||
} else {
|
||||
for (int i = 0; i < size; i++) {
|
||||
validationFailures.add(in.readString());
|
||||
}
|
||||
}
|
||||
activeShardsPercent = in.readDouble();
|
||||
}
|
||||
|
||||
|
@ -231,10 +213,6 @@ public final class ClusterStateHealth implements Iterable<ClusterIndexHealth>, S
|
|||
for (ClusterIndexHealth indexHealth : this) {
|
||||
indexHealth.writeTo(out);
|
||||
}
|
||||
out.writeVInt(validationFailures.size());
|
||||
for (String failure : validationFailures) {
|
||||
out.writeString(failure);
|
||||
}
|
||||
out.writeDouble(activeShardsPercent);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,27 +94,15 @@ public class IndexRoutingTable extends AbstractDiffable<IndexRoutingTable> imple
|
|||
return index;
|
||||
}
|
||||
|
||||
public void validate(RoutingTableValidation validation, MetaData metaData) {
|
||||
boolean validate(MetaData metaData) {
|
||||
// check index exists
|
||||
if (!metaData.hasIndex(index.getName())) {
|
||||
validation.addIndexFailure(index.getName(), "Exists in routing does not exists in metadata");
|
||||
return;
|
||||
throw new IllegalStateException(index + " exists in routing does not exists in metadata");
|
||||
}
|
||||
IndexMetaData indexMetaData = metaData.index(index.getName());
|
||||
if (indexMetaData.getIndexUUID().equals(index.getUUID()) == false) {
|
||||
validation.addIndexFailure(index.getName(), "Exists in routing does not exists in metadata with the same uuid");
|
||||
return;
|
||||
throw new IllegalStateException(index.getName() + " exists in routing does not exists in metadata with the same uuid");
|
||||
}
|
||||
for (String failure : validate(indexMetaData)) {
|
||||
validation.addIndexFailure(index.getName(), failure);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* validate based on a meta data, returning failures found
|
||||
*/
|
||||
public List<String> validate(IndexMetaData indexMetaData) {
|
||||
ArrayList<String> failures = new ArrayList<>();
|
||||
|
||||
// check the number of shards
|
||||
if (indexMetaData.getNumberOfShards() != shards().size()) {
|
||||
|
@ -125,22 +113,25 @@ public class IndexRoutingTable extends AbstractDiffable<IndexRoutingTable> imple
|
|||
for (IndexShardRoutingTable indexShardRoutingTable : this) {
|
||||
expected.remove(indexShardRoutingTable.shardId().id());
|
||||
}
|
||||
failures.add("Wrong number of shards in routing table, missing: " + expected);
|
||||
throw new IllegalStateException("Wrong number of shards in routing table, missing: " + expected);
|
||||
}
|
||||
|
||||
// check the replicas
|
||||
for (IndexShardRoutingTable indexShardRoutingTable : this) {
|
||||
int routingNumberOfReplicas = indexShardRoutingTable.size() - 1;
|
||||
if (routingNumberOfReplicas != indexMetaData.getNumberOfReplicas()) {
|
||||
failures.add("Shard [" + indexShardRoutingTable.shardId().id()
|
||||
+ "] routing table has wrong number of replicas, expected [" + indexMetaData.getNumberOfReplicas() + "], got [" + routingNumberOfReplicas + "]");
|
||||
throw new IllegalStateException("Shard [" + indexShardRoutingTable.shardId().id() +
|
||||
"] routing table has wrong number of replicas, expected [" + indexMetaData.getNumberOfReplicas() +
|
||||
"], got [" + routingNumberOfReplicas + "]");
|
||||
}
|
||||
for (ShardRouting shardRouting : indexShardRoutingTable) {
|
||||
if (!shardRouting.index().equals(index)) {
|
||||
failures.add("shard routing has an index [" + shardRouting.index() + "] that is different than the routing table");
|
||||
throw new IllegalStateException("shard routing has an index [" + shardRouting.index() + "] that is different " +
|
||||
"from the routing table");
|
||||
}
|
||||
}
|
||||
}
|
||||
return failures;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -308,9 +299,6 @@ public class IndexRoutingTable extends AbstractDiffable<IndexRoutingTable> imple
|
|||
return result;
|
||||
}
|
||||
|
||||
public void validate() throws RoutingValidationException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexRoutingTable readFrom(StreamInput in) throws IOException {
|
||||
Index index = new Index(in);
|
||||
|
@ -507,10 +495,8 @@ public class IndexRoutingTable extends AbstractDiffable<IndexRoutingTable> imple
|
|||
return this;
|
||||
}
|
||||
|
||||
public IndexRoutingTable build() throws RoutingValidationException {
|
||||
IndexRoutingTable indexRoutingTable = new IndexRoutingTable(index, shards.build());
|
||||
indexRoutingTable.validate();
|
||||
return indexRoutingTable;
|
||||
public IndexRoutingTable build() {
|
||||
return new IndexRoutingTable(index, shards.build());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -145,20 +145,13 @@ public class RoutingTable implements Iterable<IndexRoutingTable>, Diffable<Routi
|
|||
.orElse(null);
|
||||
}
|
||||
|
||||
public RoutingTable validateRaiseException(MetaData metaData) throws RoutingValidationException {
|
||||
RoutingTableValidation validation = validate(metaData);
|
||||
if (!validation.valid()) {
|
||||
throw new RoutingValidationException(validation);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoutingTableValidation validate(MetaData metaData) {
|
||||
RoutingTableValidation validation = new RoutingTableValidation();
|
||||
public boolean validate(MetaData metaData) {
|
||||
for (IndexRoutingTable indexRoutingTable : this) {
|
||||
indexRoutingTable.validate(validation, metaData);
|
||||
if (indexRoutingTable.validate(metaData) == false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return validation;
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<ShardRouting> shardsWithState(ShardRoutingState state) {
|
||||
|
@ -546,7 +539,6 @@ public class RoutingTable implements Iterable<IndexRoutingTable>, Diffable<Routi
|
|||
if (indicesRouting == null) {
|
||||
throw new IllegalStateException("once build is called the builder cannot be reused");
|
||||
}
|
||||
indexRoutingTable.validate();
|
||||
indicesRouting.put(indexRoutingTable.getIndex().getName(), indexRoutingTable);
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -1,171 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.cluster.routing;
|
||||
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Streamable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
|
||||
/**
|
||||
* Encapsulates the result of a routing table validation and provides access to
|
||||
* validation failures.
|
||||
*/
|
||||
public class RoutingTableValidation implements Streamable {
|
||||
|
||||
private boolean valid = true;
|
||||
|
||||
private List<String> failures;
|
||||
|
||||
private Map<String, List<String>> indicesFailures;
|
||||
|
||||
public RoutingTableValidation() {
|
||||
}
|
||||
|
||||
public boolean valid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
public List<String> allFailures() {
|
||||
if (failures().isEmpty() && indicesFailures().isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> allFailures = new ArrayList<>(failures());
|
||||
for (Map.Entry<String, List<String>> entry : indicesFailures().entrySet()) {
|
||||
for (String failure : entry.getValue()) {
|
||||
allFailures.add("Index [" + entry.getKey() + "]: " + failure);
|
||||
}
|
||||
}
|
||||
return allFailures;
|
||||
}
|
||||
|
||||
public List<String> failures() {
|
||||
if (failures == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return failures;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> indicesFailures() {
|
||||
if (indicesFailures == null) {
|
||||
return emptyMap();
|
||||
}
|
||||
return indicesFailures;
|
||||
}
|
||||
|
||||
public List<String> indexFailures(String index) {
|
||||
if (indicesFailures == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> indexFailures = indicesFailures.get(index);
|
||||
if (indexFailures == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return indexFailures;
|
||||
}
|
||||
|
||||
public void addFailure(String failure) {
|
||||
valid = false;
|
||||
if (failures == null) {
|
||||
failures = new ArrayList<>();
|
||||
}
|
||||
failures.add(failure);
|
||||
}
|
||||
|
||||
public void addIndexFailure(String index, String failure) {
|
||||
valid = false;
|
||||
if (indicesFailures == null) {
|
||||
indicesFailures = new HashMap<>();
|
||||
}
|
||||
List<String> indexFailures = indicesFailures.get(index);
|
||||
if (indexFailures == null) {
|
||||
indexFailures = new ArrayList<>();
|
||||
indicesFailures.put(index, indexFailures);
|
||||
}
|
||||
indexFailures.add(failure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return allFailures().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
valid = in.readBoolean();
|
||||
int size = in.readVInt();
|
||||
if (size == 0) {
|
||||
failures = Collections.emptyList();
|
||||
} else {
|
||||
failures = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
failures.add(in.readString());
|
||||
}
|
||||
}
|
||||
size = in.readVInt();
|
||||
if (size == 0) {
|
||||
indicesFailures = emptyMap();
|
||||
} else {
|
||||
indicesFailures = new HashMap<>();
|
||||
for (int i = 0; i < size; i++) {
|
||||
String index = in.readString();
|
||||
int size2 = in.readVInt();
|
||||
List<String> indexFailures = new ArrayList<>(size2);
|
||||
for (int j = 0; j < size2; j++) {
|
||||
indexFailures.add(in.readString());
|
||||
}
|
||||
indicesFailures.put(index, indexFailures);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeBoolean(valid);
|
||||
if (failures == null) {
|
||||
out.writeVInt(0);
|
||||
} else {
|
||||
out.writeVInt(failures.size());
|
||||
for (String failure : failures) {
|
||||
out.writeString(failure);
|
||||
}
|
||||
}
|
||||
if (indicesFailures == null) {
|
||||
out.writeVInt(0);
|
||||
} else {
|
||||
out.writeVInt(indicesFailures.size());
|
||||
for (Map.Entry<String, List<String>> entry : indicesFailures.entrySet()) {
|
||||
out.writeString(entry.getKey());
|
||||
out.writeVInt(entry.getValue().size());
|
||||
for (String failure : entry.getValue()) {
|
||||
out.writeString(failure);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.cluster.routing;
|
||||
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This class defines {@link RoutingException}s related to
|
||||
* the validation of routing
|
||||
*/
|
||||
public class RoutingValidationException extends RoutingException {
|
||||
|
||||
private final RoutingTableValidation validation;
|
||||
|
||||
public RoutingValidationException(RoutingTableValidation validation) {
|
||||
super(validation.toString());
|
||||
this.validation = validation;
|
||||
}
|
||||
|
||||
public RoutingValidationException(StreamInput in) throws IOException {
|
||||
super(in);
|
||||
validation = in.readOptionalStreamable(RoutingTableValidation::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeOptionalStreamable(validation);
|
||||
}
|
||||
|
||||
public RoutingTableValidation validation() {
|
||||
return this.validation;
|
||||
}
|
||||
}
|
|
@ -120,7 +120,8 @@ public class AllocationService extends AbstractComponent {
|
|||
RoutingExplanations explanations) {
|
||||
final RoutingTable newRoutingTable = new RoutingTable.Builder().updateNodes(newRoutingNodes).build();
|
||||
MetaData newMetaData = updateMetaDataWithRoutingTable(oldMetaData, oldRoutingTable, newRoutingTable);
|
||||
return new RoutingAllocation.Result(true, newRoutingTable.validateRaiseException(newMetaData), newMetaData, explanations);
|
||||
assert newRoutingTable.validate(newMetaData); // validates the routing table is coherent with the cluster state metadata
|
||||
return new RoutingAllocation.Result(true, newRoutingTable, newMetaData, explanations);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -30,8 +30,6 @@ import org.elasticsearch.cluster.block.ClusterBlockException;
|
|||
import org.elasticsearch.cluster.metadata.SnapshotId;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||
import org.elasticsearch.cluster.routing.IllegalShardRoutingStateException;
|
||||
import org.elasticsearch.cluster.routing.RoutingTableValidation;
|
||||
import org.elasticsearch.cluster.routing.RoutingValidationException;
|
||||
import org.elasticsearch.cluster.routing.ShardRouting;
|
||||
import org.elasticsearch.cluster.routing.ShardRoutingState;
|
||||
import org.elasticsearch.cluster.routing.TestShardRouting;
|
||||
|
@ -384,14 +382,6 @@ public class ExceptionSerializationTests extends ESTestCase {
|
|||
assertEquals(id, ex.id());
|
||||
}
|
||||
|
||||
public void testRoutingValidationException() throws IOException {
|
||||
RoutingTableValidation validation = new RoutingTableValidation();
|
||||
validation.addIndexFailure("foo", "bar");
|
||||
RoutingValidationException ex = serialize(new RoutingValidationException(validation));
|
||||
assertEquals("[Index [foo]: bar]", ex.getMessage());
|
||||
assertEquals(validation.toString(), ex.validation().toString());
|
||||
}
|
||||
|
||||
public void testCircuitBreakingException() throws IOException {
|
||||
CircuitBreakingException ex = serialize(new CircuitBreakingException("I hate to say I told you so...", 0, 100));
|
||||
assertEquals("I hate to say I told you so...", ex.getMessage());
|
||||
|
@ -709,7 +699,7 @@ public class ExceptionSerializationTests extends ESTestCase {
|
|||
ids.put(58, org.elasticsearch.transport.SendRequestTransportException.class);
|
||||
ids.put(59, org.elasticsearch.common.util.concurrent.EsRejectedExecutionException.class);
|
||||
ids.put(60, org.elasticsearch.common.lucene.Lucene.EarlyTerminationException.class);
|
||||
ids.put(61, org.elasticsearch.cluster.routing.RoutingValidationException.class);
|
||||
ids.put(61, null); // RoutingValidationException was removed in 5.0
|
||||
ids.put(62, org.elasticsearch.common.io.stream.NotSerializableExceptionWrapper.class);
|
||||
ids.put(63, org.elasticsearch.indices.AliasFilterParsingException.class);
|
||||
ids.put(64, null); // DeleteByQueryFailedEngineException was removed in 3.0
|
||||
|
|
|
@ -70,7 +70,6 @@ public class ClusterHealthResponsesTests extends ESTestCase {
|
|||
private void assertClusterHealth(ClusterHealthResponse clusterHealth) {
|
||||
ClusterStateHealth clusterStateHealth = clusterHealth.getClusterStateHealth();
|
||||
|
||||
assertThat(clusterHealth.getValidationFailures(), Matchers.equalTo(clusterStateHealth.getValidationFailures()));
|
||||
assertThat(clusterHealth.getActiveShards(), Matchers.equalTo(clusterStateHealth.getActiveShards()));
|
||||
assertThat(clusterHealth.getRelocatingShards(), Matchers.equalTo(clusterStateHealth.getRelocatingShards()));
|
||||
assertThat(clusterHealth.getActivePrimaryShards(), Matchers.equalTo(clusterStateHealth.getActivePrimaryShards()));
|
||||
|
|
|
@ -21,10 +21,10 @@ package org.elasticsearch.cluster.health;
|
|||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||
import org.elasticsearch.cluster.routing.IndexRoutingTable;
|
||||
import org.elasticsearch.cluster.routing.RoutingTableGenerator;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
|
||||
public class ClusterIndexHealthTests extends ESTestCase {
|
||||
public void testClusterIndexHealth() {
|
||||
|
@ -50,7 +50,6 @@ public class ClusterIndexHealthTests extends ESTestCase {
|
|||
assertThat(indexHealth.getInitializingShards(), equalTo(counter.initializing));
|
||||
assertThat(indexHealth.getUnassignedShards(), equalTo(counter.unassigned));
|
||||
assertThat(indexHealth.getShards().size(), equalTo(indexMetaData.getNumberOfShards()));
|
||||
assertThat(indexHealth.getValidationFailures(), empty());
|
||||
int totalShards = 0;
|
||||
for (ClusterShardHealth shardHealth : indexHealth.getShards().values()) {
|
||||
totalShards += shardHealth.getActiveShards() + shardHealth.getInitializingShards() + shardHealth.getUnassignedShards();
|
||||
|
@ -58,4 +57,4 @@ public class ClusterIndexHealthTests extends ESTestCase {
|
|||
|
||||
assertThat(totalShards, equalTo(indexMetaData.getNumberOfShards() * (1 + indexMetaData.getNumberOfReplicas())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
|||
import org.elasticsearch.cluster.metadata.MetaData;
|
||||
import org.elasticsearch.cluster.routing.IndexRoutingTable;
|
||||
import org.elasticsearch.cluster.routing.RoutingTable;
|
||||
import org.elasticsearch.cluster.routing.RoutingTableGenerator;
|
||||
import org.elasticsearch.cluster.service.ClusterService;
|
||||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
|
@ -42,7 +43,6 @@ import org.elasticsearch.test.gateway.NoopGatewayAllocator;
|
|||
import org.elasticsearch.test.transport.CapturingTransport;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
|
@ -57,7 +57,6 @@ import java.util.concurrent.TimeUnit;
|
|||
import static org.elasticsearch.cluster.service.ClusterServiceUtils.createClusterService;
|
||||
import static org.hamcrest.CoreMatchers.allOf;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.lessThanOrEqualTo;
|
||||
|
@ -70,7 +69,6 @@ public class ClusterStateHealthTests extends ESTestCase {
|
|||
|
||||
private ClusterService clusterService;
|
||||
private TransportService transportService;
|
||||
private CapturingTransport transport;
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
|
@ -81,9 +79,8 @@ public class ClusterStateHealthTests extends ESTestCase {
|
|||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
transport = new CapturingTransport();
|
||||
clusterService = createClusterService(threadPool);
|
||||
transportService = new TransportService(transport, threadPool);
|
||||
transportService = new TransportService(new CapturingTransport(), threadPool);
|
||||
transportService.start();
|
||||
transportService.acceptIncomingRequests();
|
||||
}
|
||||
|
@ -168,34 +165,6 @@ public class ClusterStateHealthTests extends ESTestCase {
|
|||
assertClusterHealth(clusterStateHealth, counter);
|
||||
}
|
||||
|
||||
public void testValidations() throws IOException {
|
||||
RoutingTableGenerator routingTableGenerator = new RoutingTableGenerator();
|
||||
IndexMetaData indexMetaData = IndexMetaData
|
||||
.builder("test")
|
||||
.settings(settings(Version.CURRENT))
|
||||
.numberOfShards(2)
|
||||
.numberOfReplicas(2)
|
||||
.build();
|
||||
RoutingTableGenerator.ShardCounter counter = new RoutingTableGenerator.ShardCounter();
|
||||
IndexRoutingTable indexRoutingTable = routingTableGenerator.genIndexRoutingTable(indexMetaData, counter);
|
||||
indexMetaData = IndexMetaData.builder("test").settings(settings(Version.CURRENT)).numberOfShards(2).numberOfReplicas(3).build();
|
||||
|
||||
ClusterIndexHealth indexHealth = new ClusterIndexHealth(indexMetaData, indexRoutingTable);
|
||||
assertThat(indexHealth.getValidationFailures(), Matchers.hasSize(2));
|
||||
|
||||
RoutingTable.Builder routingTable = RoutingTable.builder();
|
||||
MetaData.Builder metaData = MetaData.builder();
|
||||
metaData.put(indexMetaData, true);
|
||||
routingTable.add(indexRoutingTable);
|
||||
ClusterState clusterState = ClusterState.builder(ClusterName.DEFAULT).metaData(metaData).routingTable(routingTable.build()).build();
|
||||
String[] concreteIndices = indexNameExpressionResolver.concreteIndexNames(clusterState, IndicesOptions.strictExpand(), (String[]) null);
|
||||
ClusterStateHealth clusterStateHealth = new ClusterStateHealth(clusterState, concreteIndices);
|
||||
clusterStateHealth = maybeSerialize(clusterStateHealth);
|
||||
// currently we have no cluster level validation failures as index validation issues are reported per index.
|
||||
assertThat(clusterStateHealth.getValidationFailures(), Matchers.hasSize(0));
|
||||
}
|
||||
|
||||
|
||||
ClusterStateHealth maybeSerialize(ClusterStateHealth clusterStateHealth) throws IOException {
|
||||
if (randomBoolean()) {
|
||||
BytesStreamOutput out = new BytesStreamOutput();
|
||||
|
@ -213,7 +182,6 @@ public class ClusterStateHealthTests extends ESTestCase {
|
|||
assertThat(clusterStateHealth.getInitializingShards(), equalTo(counter.initializing));
|
||||
assertThat(clusterStateHealth.getRelocatingShards(), equalTo(counter.relocating));
|
||||
assertThat(clusterStateHealth.getUnassignedShards(), equalTo(counter.unassigned));
|
||||
assertThat(clusterStateHealth.getValidationFailures(), empty());
|
||||
assertThat(clusterStateHealth.getActiveShardsPercent(), is(allOf(greaterThanOrEqualTo(0.0), lessThanOrEqualTo(100.0))));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,19 +16,15 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.elasticsearch.cluster.health;
|
||||
package org.elasticsearch.cluster.routing;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.RandomizedContext;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.cluster.health.ClusterHealthStatus;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||
import org.elasticsearch.cluster.routing.IndexRoutingTable;
|
||||
import org.elasticsearch.cluster.routing.IndexShardRoutingTable;
|
||||
import org.elasticsearch.cluster.routing.ShardRouting;
|
||||
import org.elasticsearch.cluster.routing.ShardRoutingState;
|
||||
import org.elasticsearch.cluster.routing.TestShardRouting;
|
||||
import org.elasticsearch.index.shard.ShardId;
|
||||
|
||||
class RoutingTableGenerator {
|
||||
public class RoutingTableGenerator {
|
||||
private static int node_id = 1;
|
||||
|
||||
private ShardRouting genShardRouting(String index, int shardId, boolean primary) {
|
||||
|
@ -46,11 +42,14 @@ class RoutingTableGenerator {
|
|||
|
||||
switch (state) {
|
||||
case STARTED:
|
||||
return TestShardRouting.newShardRouting(index, shardId, "node_" + Integer.toString(node_id++), null, null, primary, ShardRoutingState.STARTED);
|
||||
return TestShardRouting.newShardRouting(index, shardId, "node_" + Integer.toString(node_id++),
|
||||
null, null, primary, ShardRoutingState.STARTED);
|
||||
case INITIALIZING:
|
||||
return TestShardRouting.newShardRouting(index, shardId, "node_" + Integer.toString(node_id++), null, null, primary, ShardRoutingState.INITIALIZING);
|
||||
return TestShardRouting.newShardRouting(index, shardId, "node_" + Integer.toString(node_id++),
|
||||
null, null, primary, ShardRoutingState.INITIALIZING);
|
||||
case RELOCATING:
|
||||
return TestShardRouting.newShardRouting(index, shardId, "node_" + Integer.toString(node_id++), "node_" + Integer.toString(node_id++), null, primary, ShardRoutingState.RELOCATING);
|
||||
return TestShardRouting.newShardRouting(index, shardId, "node_" + Integer.toString(node_id++),
|
||||
"node_" + Integer.toString(node_id++), null, primary, ShardRoutingState.RELOCATING);
|
||||
default:
|
||||
throw new ElasticsearchException("Unknown state: " + state.name());
|
||||
}
|
||||
|
@ -74,12 +73,13 @@ class RoutingTableGenerator {
|
|||
public IndexRoutingTable genIndexRoutingTable(IndexMetaData indexMetaData, ShardCounter counter) {
|
||||
IndexRoutingTable.Builder builder = IndexRoutingTable.builder(indexMetaData.getIndex());
|
||||
for (int shard = 0; shard < indexMetaData.getNumberOfShards(); shard++) {
|
||||
builder.addIndexShard(genShardRoutingTable(indexMetaData.getIndex().getName(), shard, indexMetaData.getNumberOfReplicas(), counter));
|
||||
builder.addIndexShard(genShardRoutingTable(indexMetaData.getIndex().getName(), shard,
|
||||
indexMetaData.getNumberOfReplicas(), counter));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
static class ShardCounter {
|
||||
public static class ShardCounter {
|
||||
public int active;
|
||||
public int relocating;
|
||||
public int initializing;
|
|
@ -286,4 +286,45 @@ public class RoutingTableTests extends ESAllocationTestCase {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
public void testValidations() {
|
||||
final String indexName = "test";
|
||||
final int numShards = 1;
|
||||
final int numReplicas = randomIntBetween(0, 1);
|
||||
IndexMetaData indexMetaData = IndexMetaData.builder(indexName)
|
||||
.settings(settings(Version.CURRENT))
|
||||
.numberOfShards(numShards)
|
||||
.numberOfReplicas(numReplicas)
|
||||
.build();
|
||||
MetaData metaData = MetaData.builder().put(indexMetaData, true).build();
|
||||
final RoutingTableGenerator routingTableGenerator = new RoutingTableGenerator();
|
||||
final RoutingTableGenerator.ShardCounter counter = new RoutingTableGenerator.ShardCounter();
|
||||
final IndexRoutingTable indexRoutingTable = routingTableGenerator.genIndexRoutingTable(indexMetaData, counter);
|
||||
// test no validation errors
|
||||
assertTrue(indexRoutingTable.validate(metaData));
|
||||
// test wrong number of shards causes validation errors
|
||||
indexMetaData = IndexMetaData.builder(indexName)
|
||||
.settings(settings(Version.CURRENT))
|
||||
.numberOfShards(numShards + 1)
|
||||
.numberOfReplicas(numReplicas)
|
||||
.build();
|
||||
final MetaData metaData2 = MetaData.builder().put(indexMetaData, true).build();
|
||||
expectThrows(IllegalStateException.class, () -> indexRoutingTable.validate(metaData2));
|
||||
// test wrong number of replicas causes validation errors
|
||||
indexMetaData = IndexMetaData.builder(indexName)
|
||||
.settings(settings(Version.CURRENT))
|
||||
.numberOfShards(numShards)
|
||||
.numberOfReplicas(numReplicas + 1)
|
||||
.build();
|
||||
final MetaData metaData3 = MetaData.builder().put(indexMetaData, true).build();
|
||||
expectThrows(IllegalStateException.class, () -> indexRoutingTable.validate(metaData3));
|
||||
// test wrong number of shards and replicas causes validation errors
|
||||
indexMetaData = IndexMetaData.builder(indexName)
|
||||
.settings(settings(Version.CURRENT))
|
||||
.numberOfShards(numShards + 1)
|
||||
.numberOfReplicas(numReplicas + 1)
|
||||
.build();
|
||||
final MetaData metaData4 = MetaData.builder().put(indexMetaData, true).build();
|
||||
expectThrows(IllegalStateException.class, () -> indexRoutingTable.validate(metaData4));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue