Implement toXContent on ShardOpertionFailureException

ShardOperationFailureException implementations alread provide structured
exception support but it's not yet exposed on the interface. This change
allows nice rendering of structured REST exceptions also if searches fail on
only a subset of the shards etc.

Closes #11017
This commit is contained in:
Simon Willnauer 2015-05-18 09:54:04 +02:00
parent f7696ec149
commit 8b8ba9a44f
27 changed files with 181 additions and 129 deletions

View File

@ -22,16 +22,18 @@ package org.elasticsearch;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.elasticsearch.action.ShardOperationFailedException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexException;
import org.elasticsearch.rest.HasRestHeaders;
import org.elasticsearch.rest.RestStatus;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* A base class for all elasticsearch exceptions.
@ -288,6 +290,4 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
public String toString() {
return ExceptionsHelper.detailedMessage(this).trim();
}
}

View File

@ -22,15 +22,21 @@ package org.elasticsearch;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexFormatTooNewException;
import org.apache.lucene.index.IndexFormatTooOldException;
import org.elasticsearch.action.ShardOperationFailedException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexException;
import org.elasticsearch.rest.RestStatus;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
*
@ -214,4 +220,59 @@ public final class ExceptionsHelper {
}
return true;
}
/**
* Deduplicate the failures by exception message and index.
*/
public static ShardOperationFailedException[] groupBy(ShardOperationFailedException[] failures) {
List<ShardOperationFailedException> uniqueFailures = new ArrayList<>();
Set<GroupBy> reasons = new HashSet<>();
for (ShardOperationFailedException failure : failures) {
GroupBy reason = new GroupBy(failure.getCause());
if (reasons.contains(reason) == false) {
reasons.add(reason);
uniqueFailures.add(failure);
}
}
return uniqueFailures.toArray(new ShardOperationFailedException[0]);
}
static class GroupBy {
final String reason;
final Index index;
final Class<? extends Throwable> causeType;
public GroupBy(Throwable t) {
if (t instanceof IndexException) {
index = ((IndexException) t).index();
} else {
index = null;
}
reason = t.getMessage();
causeType = t.getClass();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GroupBy groupBy = (GroupBy) o;
if (!causeType.equals(groupBy.causeType)) return false;
if (index != null ? !index.equals(groupBy.index) : groupBy.index != null) return false;
if (reason != null ? !reason.equals(groupBy.reason) : groupBy.reason != null) return false;
return true;
}
@Override
public int hashCode() {
int result = reason != null ? reason.hashCode() : 0;
result = 31 * result + (index != null ? index.hashCode() : 0);
result = 31 * result + causeType.hashCode();
return result;
}
}
}

View File

@ -231,6 +231,11 @@ public abstract class ActionWriteResponse extends ActionResponse {
return status;
}
@Override
public Throwable getCause() {
return cause;
}
/**
* @return Whether this failure occurred on a primary shard.
* (this only reports true for delete by query)

View File

@ -20,16 +20,23 @@
package org.elasticsearch.action;
import org.elasticsearch.common.io.stream.Streamable;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexException;
import org.elasticsearch.rest.RestStatus;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* An exception indicating that a failure occurred performing an operation on the shard.
*
*
*/
public interface ShardOperationFailedException extends Streamable, Serializable {
public interface ShardOperationFailedException extends Streamable, Serializable, ToXContent {
/**
* The index the operation failed on. Might return <tt>null</tt> if it can't be derived.
@ -50,4 +57,9 @@ public interface ShardOperationFailedException extends Streamable, Serializable
* The status of the failure.
*/
RestStatus status();
/**
* The cause of this failure
*/
Throwable getCause();
}

View File

@ -19,6 +19,7 @@
package org.elasticsearch.action.bulk;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.action.ActionWriteResponse;
import org.elasticsearch.action.delete.DeleteResponse;
@ -27,6 +28,7 @@ import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Streamable;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.rest.RestStatus;
import java.io.IOException;
@ -44,26 +46,17 @@ public class BulkItemResponse implements Streamable {
private final String index;
private final String type;
private final String id;
private final String message;
private final Throwable cause;
private final RestStatus status;
public Failure(String index, String type, String id, Throwable t) {
this.index = index;
this.type = type;
this.id = id;
this.message = t.toString();
this.cause = t;
this.status = ExceptionsHelper.status(t);
}
public Failure(String index, String type, String id, String message, RestStatus status) {
this.index = index;
this.type = type;
this.id = id;
this.message = message;
this.status = status;
}
/**
* The index name of the action.
*/
@ -89,7 +82,7 @@ public class BulkItemResponse implements Streamable {
* The failure message.
*/
public String getMessage() {
return this.message;
return this.cause.toString();
}
/**
@ -98,6 +91,10 @@ public class BulkItemResponse implements Streamable {
public RestStatus getStatus() {
return this.status;
}
public Throwable getCause() {
return cause;
}
}
private int id;
@ -265,9 +262,8 @@ public class BulkItemResponse implements Streamable {
String fIndex = in.readString();
String fType = in.readString();
String fId = in.readOptionalString();
String fMessage = in.readString();
RestStatus status = RestStatus.readFrom(in);
failure = new Failure(fIndex, fType, fId, fMessage, status);
Throwable throwable = in.readThrowable();
failure = new Failure(fIndex, fType, fId, throwable);
}
}
@ -295,8 +291,7 @@ public class BulkItemResponse implements Streamable {
out.writeString(failure.getIndex());
out.writeString(failure.getType());
out.writeOptionalString(failure.getId());
out.writeString(failure.getMessage());
RestStatus.writeTo(out, failure.getStatus());
out.writeThrowable(failure.getCause());
}
}
}

View File

@ -284,7 +284,7 @@ public class TransportBulkAction extends HandledTransportAction<BulkRequest, Bul
MappingMetaData mappingMd = clusterState.metaData().index(concreteIndex).mappingOrDefault(updateRequest.type());
if (mappingMd != null && mappingMd.routing().required() && updateRequest.routing() == null) {
BulkItemResponse.Failure failure = new BulkItemResponse.Failure(updateRequest.index(), updateRequest.type(),
updateRequest.id(), "routing is required for this item", RestStatus.BAD_REQUEST);
updateRequest.id(), new IllegalArgumentException("routing is required for this item"));
responses.set(i, new BulkItemResponse(i, updateRequest.type(), failure));
continue;
}
@ -328,21 +328,19 @@ public class TransportBulkAction extends HandledTransportAction<BulkRequest, Bul
@Override
public void onFailure(Throwable e) {
// create failures for all relevant requests
String message = ExceptionsHelper.detailedMessage(e);
RestStatus status = ExceptionsHelper.status(e);
for (BulkItemRequest request : requests) {
if (request.request() instanceof IndexRequest) {
IndexRequest indexRequest = (IndexRequest) request.request();
responses.set(request.id(), new BulkItemResponse(request.id(), indexRequest.opType().toString().toLowerCase(Locale.ENGLISH),
new BulkItemResponse.Failure(concreteIndices.getConcreteIndex(indexRequest.index()), indexRequest.type(), indexRequest.id(), message, status)));
new BulkItemResponse.Failure(concreteIndices.getConcreteIndex(indexRequest.index()), indexRequest.type(), indexRequest.id(), e)));
} else if (request.request() instanceof DeleteRequest) {
DeleteRequest deleteRequest = (DeleteRequest) request.request();
responses.set(request.id(), new BulkItemResponse(request.id(), "delete",
new BulkItemResponse.Failure(concreteIndices.getConcreteIndex(deleteRequest.index()), deleteRequest.type(), deleteRequest.id(), message, status)));
new BulkItemResponse.Failure(concreteIndices.getConcreteIndex(deleteRequest.index()), deleteRequest.type(), deleteRequest.id(), e)));
} else if (request.request() instanceof UpdateRequest) {
UpdateRequest updateRequest = (UpdateRequest) request.request();
responses.set(request.id(), new BulkItemResponse(request.id(), "update",
new BulkItemResponse.Failure(concreteIndices.getConcreteIndex(updateRequest.index()), updateRequest.type(), updateRequest.id(), message, status)));
new BulkItemResponse.Failure(concreteIndices.getConcreteIndex(updateRequest.index()), updateRequest.type(), updateRequest.id(), e)));
}
}
if (counter.decrementAndGet() == 0) {

View File

@ -116,7 +116,7 @@ public class PercolateResponse extends BroadcastOperationResponse implements Ite
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.field(Fields.TOOK, tookInMillis);
RestActions.buildBroadcastShardsHeader(builder, this);
RestActions.buildBroadcastShardsHeader(builder, params, this);
builder.field(Fields.TOTAL, count);
if (matches != null) {

View File

@ -20,6 +20,8 @@
package org.elasticsearch.action.search;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.action.ShardOperationFailedException;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexException;
@ -92,8 +94,8 @@ public class SearchPhaseExecutionException extends ElasticsearchException {
builder.field("grouped", group); // notify that it's grouped
builder.field("failed_shards");
builder.startArray();
ShardSearchFailure[] failures = params.paramAsBoolean("group_shard_failures", true) ? groupBy(shardFailures) : shardFailures;
for (ShardSearchFailure failure : failures) {
ShardOperationFailedException[] failures = params.paramAsBoolean("group_shard_failures", true) ? ExceptionsHelper.groupBy(shardFailures) : shardFailures;
for (ShardOperationFailedException failure : failures) {
builder.startObject();
failure.toXContent(builder, params);
builder.endObject();
@ -103,25 +105,11 @@ public class SearchPhaseExecutionException extends ElasticsearchException {
}
private ShardSearchFailure[] groupBy(ShardSearchFailure[] failures) {
List<ShardSearchFailure> uniqueFailures = new ArrayList<>();
Set<GroupBy> reasons = new HashSet<>();
for (ShardSearchFailure failure : failures) {
GroupBy reason = new GroupBy(failure.getCause());
if (reasons.contains(reason) == false) {
reasons.add(reason);
uniqueFailures.add(failure);
}
}
return uniqueFailures.toArray(new ShardSearchFailure[0]);
}
@Override
public ElasticsearchException[] guessRootCauses() {
ShardSearchFailure[] failures = groupBy(shardFailures);
ShardOperationFailedException[] failures = ExceptionsHelper.groupBy(shardFailures);
List<ElasticsearchException> rootCauses = new ArrayList<>(failures.length);
for (ShardSearchFailure failure : failures) {
for (ShardOperationFailedException failure : failures) {
ElasticsearchException[] guessRootCauses = ElasticsearchException.guessRootCauses(failure.getCause());
rootCauses.addAll(Arrays.asList(guessRootCauses));
}
@ -132,42 +120,4 @@ public class SearchPhaseExecutionException extends ElasticsearchException {
public String toString() {
return buildMessage(phaseName, getMessage(), shardFailures);
}
static class GroupBy {
final String reason;
final Index index;
final Class<? extends Throwable> causeType;
public GroupBy(Throwable t) {
if (t instanceof IndexException) {
index = ((IndexException) t).index();
} else {
index = null;
}
reason = t.getMessage();
causeType = t.getClass();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GroupBy groupBy = (GroupBy) o;
if (!causeType.equals(groupBy.causeType)) return false;
if (index != null ? !index.equals(groupBy.index) : groupBy.index != null) return false;
if (reason != null ? !reason.equals(groupBy.reason) : groupBy.reason != null) return false;
return true;
}
@Override
public int hashCode() {
int result = reason != null ? reason.hashCode() : 0;
result = 31 * result + (index != null ? index.hashCode() : 0);
result = 31 * result + causeType.hashCode();
return result;
}
}
}

View File

@ -177,7 +177,7 @@ public class SearchResponse extends ActionResponse implements StatusToXContent {
if (isTerminatedEarly() != null) {
builder.field(Fields.TERMINATED_EARLY, isTerminatedEarly());
}
RestActions.buildBroadcastShardsHeader(builder, getTotalShards(), getSuccessfulShards(), getFailedShards(), getShardFailures());
RestActions.buildBroadcastShardsHeader(builder, params, getTotalShards(), getSuccessfulShards(), getFailedShards(), getShardFailures());
internalResponse.toXContent(builder, params);
return builder;
}

View File

@ -40,7 +40,7 @@ import static org.elasticsearch.search.SearchShardTarget.readSearchShardTarget;
/**
* Represents a failure to search on a specific shard.
*/
public class ShardSearchFailure implements ShardOperationFailedException, ToXContent {
public class ShardSearchFailure implements ShardOperationFailedException {
public static final ShardSearchFailure[] EMPTY_ARRAY = new ShardSearchFailure[0];
@ -172,6 +172,7 @@ public class ShardSearchFailure implements ShardOperationFailedException, ToXCon
return builder;
}
@Override
public Throwable getCause() {
return cause;
}

View File

@ -24,6 +24,8 @@ import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.action.ShardOperationFailedException;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.shard.IndexShardException;
import org.elasticsearch.rest.RestStatus;
@ -81,6 +83,11 @@ public class DefaultShardOperationFailedException implements ShardOperationFaile
return status;
}
@Override
public Throwable getCause() {
return reason;
}
public static DefaultShardOperationFailedException readShardOperationFailed(StreamInput in) throws IOException {
DefaultShardOperationFailedException exp = new DefaultShardOperationFailedException();
exp.readFrom(in);
@ -114,4 +121,19 @@ public class DefaultShardOperationFailedException implements ShardOperationFaile
public String toString() {
return "[" + index + "][" + shardId + "] failed, reason [" + reason() + "]";
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.field("shard", shardId());
builder.field("index", index());
builder.field("status", status.name());
if (reason != null) {
builder.field("reason");
builder.startObject();
ElasticsearchException.toXContent(builder, params, reason);
builder.endObject();
}
return builder;
}
}

View File

@ -62,7 +62,7 @@ public class RestClearIndicesCacheAction extends BaseRestHandler {
@Override
public RestResponse buildResponse(ClearIndicesCacheResponse response, XContentBuilder builder) throws Exception {
builder.startObject();
buildBroadcastShardsHeader(builder, response);
buildBroadcastShardsHeader(builder, request, response);
builder.endObject();
return new BytesRestResponse(OK, builder);
}

View File

@ -60,7 +60,7 @@ public class RestFlushAction extends BaseRestHandler {
@Override
public RestResponse buildResponse(FlushResponse response, XContentBuilder builder) throws Exception {
builder.startObject();
buildBroadcastShardsHeader(builder, response);
buildBroadcastShardsHeader(builder, request, response);
builder.endObject();
return new BytesRestResponse(OK, builder);
}

View File

@ -61,7 +61,7 @@ public class RestOptimizeAction extends BaseRestHandler {
@Override
public RestResponse buildResponse(OptimizeResponse response, XContentBuilder builder) throws Exception {
builder.startObject();
buildBroadcastShardsHeader(builder, response);
buildBroadcastShardsHeader(builder, request, response);
builder.endObject();
return new BytesRestResponse(OK, builder);
}

View File

@ -58,7 +58,7 @@ public class RestRefreshAction extends BaseRestHandler {
@Override
public RestResponse buildResponse(RefreshResponse response, XContentBuilder builder) throws Exception {
builder.startObject();
buildBroadcastShardsHeader(builder, response);
buildBroadcastShardsHeader(builder, request, response);
builder.endObject();
return new BytesRestResponse(OK, builder);
}

View File

@ -54,7 +54,7 @@ public class RestIndicesSegmentsAction extends BaseRestHandler {
@Override
public RestResponse buildResponse(IndicesSegmentResponse response, XContentBuilder builder) throws Exception {
builder.startObject();
buildBroadcastShardsHeader(builder, response);
buildBroadcastShardsHeader(builder, request, response);
response.toXContent(builder, request);
builder.endObject();
return new BytesRestResponse(OK, builder);

View File

@ -103,7 +103,7 @@ public class RestIndicesStatsAction extends BaseRestHandler {
@Override
public RestResponse buildResponse(IndicesStatsResponse response, XContentBuilder builder) throws Exception {
builder.startObject();
buildBroadcastShardsHeader(builder, response);
buildBroadcastShardsHeader(builder, request, response);
response.toXContent(builder, request);
builder.endObject();
return new BytesRestResponse(OK, builder);

View File

@ -87,7 +87,7 @@ public class RestUpgradeAction extends BaseRestHandler {
});
}
void handlePost(RestRequest request, RestChannel channel, Client client) {
void handlePost(final RestRequest request, RestChannel channel, Client client) {
OptimizeRequest optimizeReq = new OptimizeRequest(Strings.splitStringByCommaToArray(request.param("index")));
optimizeReq.flush(true);
optimizeReq.upgrade(true);
@ -97,7 +97,7 @@ public class RestUpgradeAction extends BaseRestHandler {
@Override
public RestResponse buildResponse(OptimizeResponse response, XContentBuilder builder) throws Exception {
builder.startObject();
buildBroadcastShardsHeader(builder, response);
buildBroadcastShardsHeader(builder, request, response);
builder.endObject();
return new BytesRestResponse(OK, builder);
}

View File

@ -84,7 +84,7 @@ public class RestValidateQueryAction extends BaseRestHandler {
builder.startObject();
builder.field("valid", response.isValid());
buildBroadcastShardsHeader(builder, response);
buildBroadcastShardsHeader(builder, request, response);
if (response.getQueryExplanation() != null && !response.getQueryExplanation().isEmpty()) {
builder.startArray("explanations");

View File

@ -19,6 +19,7 @@
package org.elasticsearch.rest.action.bulk;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.ActionWriteResponse;
import org.elasticsearch.action.WriteConsistencyLevel;
import org.elasticsearch.action.bulk.BulkItemResponse;
@ -102,7 +103,9 @@ public class RestBulkAction extends BaseRestHandler {
}
if (itemResponse.isFailed()) {
builder.field(Fields.STATUS, itemResponse.getFailure().getStatus().getStatus());
builder.field(Fields.ERROR, itemResponse.getFailure().getMessage());
builder.startObject(Fields.ERROR);
ElasticsearchException.toXContent(builder, request, itemResponse.getFailure().getCause());
builder.endObject();
} else {
ActionWriteResponse.ShardInfo shardInfo = itemResponse.getResponse().getShardInfo();
shardInfo.toXContent(builder, request);

View File

@ -85,7 +85,7 @@ public class RestCountAction extends BaseRestHandler {
builder.field("terminated_early", response.terminatedEarly());
}
builder.field("count", response.getCount());
buildBroadcastShardsHeader(builder, response);
buildBroadcastShardsHeader(builder, request, response);
builder.endObject();
return new BytesRestResponse(response.status(), builder);

View File

@ -62,7 +62,7 @@ public class RestFieldStatsAction extends BaseRestHandler {
@Override
public RestResponse buildResponse(FieldStatsResponse response, XContentBuilder builder) throws Exception {
builder.startObject();
buildBroadcastShardsHeader(builder, response);
buildBroadcastShardsHeader(builder, request, response);
builder.startObject("indices");
for (Map.Entry<String, Map<String, FieldStats>> entry1 : response.getIndicesMergedFieldStats().entrySet()) {

View File

@ -72,7 +72,7 @@ public class RestSuggestAction extends BaseRestHandler {
public RestResponse buildResponse(SuggestResponse response, XContentBuilder builder) throws Exception {
RestStatus restStatus = RestStatus.status(response.getSuccessfulShards(), response.getTotalShards(), response.getShardFailures());
builder.startObject();
buildBroadcastShardsHeader(builder, response);
buildBroadcastShardsHeader(builder, request, response);
Suggest suggest = response.getSuggest();
if (suggest != null) {
suggest.toXContent(builder, request);

View File

@ -19,16 +19,14 @@
package org.elasticsearch.rest.action.support;
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.action.ShardOperationFailedException;
import org.elasticsearch.action.support.QuerySourceBuilder;
import org.elasticsearch.action.support.broadcast.BroadcastOperationResponse;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.lucene.uid.Versions;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentBuilderString;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.*;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.QueryStringQueryBuilder;
import org.elasticsearch.rest.RestRequest;
@ -62,33 +60,23 @@ public class RestActions {
static final XContentBuilderString SUCCESSFUL = new XContentBuilderString("successful");
static final XContentBuilderString FAILED = new XContentBuilderString("failed");
static final XContentBuilderString FAILURES = new XContentBuilderString("failures");
static final XContentBuilderString INDEX = new XContentBuilderString("index");
static final XContentBuilderString SHARD = new XContentBuilderString("shard");
static final XContentBuilderString STATUS = new XContentBuilderString("status");
static final XContentBuilderString REASON = new XContentBuilderString("reason");
}
public static void buildBroadcastShardsHeader(XContentBuilder builder, BroadcastOperationResponse response) throws IOException {
buildBroadcastShardsHeader(builder, response.getTotalShards(), response.getSuccessfulShards(), response.getFailedShards(), response.getShardFailures());
public static void buildBroadcastShardsHeader(XContentBuilder builder, ToXContent.Params params, BroadcastOperationResponse response) throws IOException {
buildBroadcastShardsHeader(builder, params, response.getTotalShards(), response.getSuccessfulShards(), response.getFailedShards(), response.getShardFailures());
}
public static void buildBroadcastShardsHeader(XContentBuilder builder, int total, int successful, int failed, ShardOperationFailedException[] shardFailures) throws IOException {
public static void buildBroadcastShardsHeader(XContentBuilder builder, ToXContent.Params params, int total, int successful, int failed, ShardOperationFailedException[] shardFailures) throws IOException {
builder.startObject(Fields._SHARDS);
builder.field(Fields.TOTAL, total);
builder.field(Fields.SUCCESSFUL, successful);
builder.field(Fields.FAILED, failed);
if (shardFailures != null && shardFailures.length > 0) {
builder.startArray(Fields.FAILURES);
for (ShardOperationFailedException shardFailure : shardFailures) {
final boolean group = params.paramAsBoolean("group_shard_failures", true); // we group by default
for (ShardOperationFailedException shardFailure : group ? ExceptionsHelper.groupBy(shardFailures) : shardFailures) {
builder.startObject();
if (shardFailure.index() != null) {
builder.field(Fields.INDEX, shardFailure.index(), XContentBuilder.FieldCaseConversion.NONE);
}
if (shardFailure.shardId() != -1) {
builder.field(Fields.SHARD, shardFailure.shardId());
}
builder.field(Fields.STATUS, shardFailure.status().getStatus());
builder.field(Fields.REASON, shardFailure.reason());
shardFailure.toXContent(builder, params);
builder.endObject();
}
builder.endArray();

View File

@ -255,7 +255,9 @@ public class Snapshot implements Comparable<Snapshot>, ToXContent {
builder.field(Fields.SUCCESSFUL_SHARDS, successfulShards);
builder.startArray(Fields.FAILURES);
for (SnapshotShardFailure shardFailure : shardFailures) {
SnapshotShardFailure.toXContent(shardFailure, builder, params);
builder.startObject();
shardFailure.toXContent(builder, params);
builder.endObject();
}
builder.endArray();
builder.endObject();

View File

@ -223,7 +223,9 @@ public class SnapshotInfo implements ToXContent, Streamable {
}
builder.startArray(Fields.FAILURES);
for (SnapshotShardFailure shardFailure : shardFailures) {
SnapshotShardFailure.toXContent(shardFailure, builder, params);
builder.startObject();
shardFailure.toXContent(builder, params);
builder.endObject();
}
builder.endArray();
builder.startObject(Fields.SHARDS);

View File

@ -27,6 +27,8 @@ import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.index.snapshots.IndexShardSnapshotFailedException;
import org.elasticsearch.rest.RestStatus;
import java.io.IOException;
@ -106,6 +108,11 @@ public class SnapshotShardFailure implements ShardOperationFailedException {
return status;
}
@Override
public Throwable getCause() {
return new IndexShardSnapshotFailedException(new ShardId(index, shardId), reason);
}
/**
* Returns node id where failure occurred
*
@ -162,13 +169,7 @@ public class SnapshotShardFailure implements ShardOperationFailedException {
*/
public static void toXContent(SnapshotShardFailure snapshotShardFailure, XContentBuilder builder, ToXContent.Params params) throws IOException {
builder.startObject();
if (snapshotShardFailure.nodeId != null) {
builder.field("node_id", snapshotShardFailure.nodeId);
}
builder.field("index", snapshotShardFailure.index);
builder.field("reason", snapshotShardFailure.reason);
builder.field("shard_id", snapshotShardFailure.shardId);
builder.field("status", snapshotShardFailure.status.name());
snapshotShardFailure.toXContent(builder, params);
builder.endObject();
}
@ -212,4 +213,16 @@ public class SnapshotShardFailure implements ShardOperationFailedException {
}
return snapshotShardFailure;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.field("index", index);
builder.field("shard_id", shardId);
builder.field("reason", reason);
if (nodeId != null) {
builder.field("node_id", nodeId);
}
builder.field("status", status.name());
return builder;
}
}