Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,34 @@
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.action.ShardOperationFailedException;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchException;
import org.elasticsearch.search.SearchShardTarget;

import java.io.IOException;

import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken;
import static org.elasticsearch.common.xcontent.XContentParserUtils.throwUnknownField;
import static org.elasticsearch.common.xcontent.XContentParserUtils.throwUnknownToken;

/**
* Represents a failure to search on a specific shard.
*/
public class ShardSearchFailure implements ShardOperationFailedException {

private static final String REASON_FIELD = "reason";
private static final String NODE_FIELD = "node";
private static final String INDEX_FIELD = "index";
private static final String SHARD_FIELD = "shard";

public static final ShardSearchFailure[] EMPTY_ARRAY = new ShardSearchFailure[0];

private SearchShardTarget shardTarget;
Expand Down Expand Up @@ -68,7 +81,7 @@ public ShardSearchFailure(String reason, SearchShardTarget shardTarget) {
this(reason, shardTarget, RestStatus.INTERNAL_SERVER_ERROR);
}

public ShardSearchFailure(String reason, SearchShardTarget shardTarget, RestStatus status) {
private ShardSearchFailure(String reason, SearchShardTarget shardTarget, RestStatus status) {
this.shardTarget = shardTarget;
this.reason = reason;
this.status = status;
Expand Down Expand Up @@ -153,20 +166,55 @@ public void writeTo(StreamOutput out) throws IOException {

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.field("shard", shardId());
builder.field("index", index());
builder.field(SHARD_FIELD, shardId());
builder.field(INDEX_FIELD, index());
if (shardTarget != null) {
builder.field("node", shardTarget.getNodeId());
builder.field(NODE_FIELD, shardTarget.getNodeId());
}
if (cause != null) {
builder.field("reason");
builder.field(REASON_FIELD);
builder.startObject();
ElasticsearchException.generateThrowableXContent(builder, params, cause);
builder.endObject();
}
return builder;
}

public static ShardSearchFailure fromXContent(XContentParser parser) throws IOException {
XContentParser.Token token;
ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.currentToken(), parser::getTokenLocation);
String currentFieldName = null;
int shardId = -1;
String indexName = null;
String nodeId = null;
ElasticsearchException exception = null;
while((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token.isValue()) {
if (SHARD_FIELD.equals(currentFieldName)) {
shardId = parser.intValue();
} else if (INDEX_FIELD.equals(currentFieldName)) {
indexName = parser.text();
} else if (NODE_FIELD.equals(currentFieldName)) {
nodeId = parser.text();
} else {
throwUnknownField(currentFieldName, parser.getTokenLocation());
}
} else if (token == XContentParser.Token.START_OBJECT) {
if (REASON_FIELD.equals(currentFieldName)) {
exception = ElasticsearchException.fromXContent(parser);
} else {
throwUnknownField(currentFieldName, parser.getTokenLocation());
}
} else {
throwUnknownToken(token, parser.getTokenLocation());
}
}
return new ShardSearchFailure(exception,
new SearchShardTarget(nodeId, new ShardId(new Index(indexName, IndexMetaData.INDEX_UUID_NA_VALUE), shardId)));
}

@Override
public Throwable getCause() {
return cause;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* 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.action.search;

import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.search.SearchShardTarget;
import org.elasticsearch.test.ESTestCase;

import java.io.IOException;

import static org.elasticsearch.common.xcontent.XContentHelper.toXContent;

public class ShardSearchFailureTests extends ESTestCase {

public static ShardSearchFailure createTestItem() {
String randomMessage = randomAsciiOfLengthBetween(3, 20);
Exception ex = new ParsingException(0, 0, randomMessage , new IllegalArgumentException("some bad argument"));
String nodeId = randomAsciiOfLengthBetween(5, 10);
String indexName = randomAsciiOfLengthBetween(5, 10);
String indexUuid = randomAsciiOfLengthBetween(5, 10);
int shardId = randomInt();
return new ShardSearchFailure(ex,
new SearchShardTarget(nodeId, new ShardId(new Index(indexName, indexUuid), shardId)));
}

public void testFromXContent() throws IOException {
ShardSearchFailure response = createTestItem();
XContentType xContentType = randomFrom(XContentType.values());
boolean humanReadable = randomBoolean();
BytesReference originalBytes = toXContent(response, xContentType, humanReadable);

ShardSearchFailure parsed;
try (XContentParser parser = createParser(xContentType.xContent(), originalBytes)) {
assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken());
parsed = ShardSearchFailure.fromXContent(parser);
assertEquals(XContentParser.Token.END_OBJECT, parser.currentToken());
assertNull(parser.nextToken());
}
assertEquals(response.index(), parsed.index());
assertEquals(response.shard().getNodeId(), parsed.shard().getNodeId());
assertEquals(response.shardId(), parsed.shardId());

// we cannot compare the cause, because it will be wrapped in an outer ElasticSearchException
// best effort: try to check that the original message appears somewhere in the rendered xContent
String originalMsg = response.getCause().getMessage();
assertEquals(parsed.getCause().getMessage(), "Elasticsearch exception [type=parsing_exception, reason=" + originalMsg + "]");
String nestedMsg = response.getCause().getCause().getMessage();
assertEquals(parsed.getCause().getCause().getMessage(),
"Elasticsearch exception [type=illegal_argument_exception, reason=" + nestedMsg + "]");
}

public void testToXContent() throws IOException {
ShardSearchFailure failure = new ShardSearchFailure(new ParsingException(0, 0, "some message", null),
new SearchShardTarget("nodeId", new ShardId(new Index("indexName", "indexUuid"), 123)));
BytesReference xContent = toXContent(failure, XContentType.JSON);
assertEquals(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shall we rather use assertToXContentEquivalent ? I am always cautious on comparing json strings (keys ordering etc.) but maybe in this case it's ok

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in the toXContent tests that are more a way of checking the intended output is as expected (and more to show a simple, fixed example output in the tests), string comparrison is more intuitive and shouldn't break. We also do this in the other toXContent tests, so I will leave this also to stay consistent.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no objections :)

"{\"shard\":123,"
+ "\"index\":\"indexName\","
+ "\"node\":\"nodeId\","
+ "\"reason\":{"
+ "\"type\":\"parsing_exception\","
+ "\"reason\":\"some message\","
+ "\"line\":0,"
+ "\"col\":0"
+ "}"
+ "}",
xContent.utf8ToString());
}
}