Skip to content

Commit d673339

Browse files
Cleanup ElasticsearchException a little (#127713)
Looked into this logic a bit since I suspect it of causing some OOMs potentially. We should probably be using chunked writing for these in REST responses, but for now a couple smaller cleanups and savings only.
1 parent aee4465 commit d673339

File tree

2 files changed

+52
-80
lines changed

2 files changed

+52
-80
lines changed

server/src/main/java/org/elasticsearch/ElasticsearchException.java

+34-52
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ public Throwable unwrapCause() {
294294
public String getDetailedMessage() {
295295
if (getCause() != null) {
296296
StringBuilder sb = new StringBuilder();
297-
sb.append(toString()).append("; ");
297+
sb.append(this).append("; ");
298298
if (getCause() instanceof ElasticsearchException) {
299299
sb.append(((ElasticsearchException) getCause()).getDetailedMessage());
300300
} else {
@@ -384,7 +384,7 @@ protected XContentBuilder toXContent(XContentBuilder builder, Params params, int
384384
if (ex != this) {
385385
generateThrowableXContent(builder, params, this, nestedLevel);
386386
} else {
387-
innerToXContent(builder, params, this, getExceptionName(), getMessage(), headers, metadata, getCause(), nestedLevel);
387+
innerToXContent(builder, params, this, headers, metadata, getCause(), nestedLevel);
388388
}
389389
return builder;
390390
}
@@ -393,8 +393,6 @@ protected static void innerToXContent(
393393
XContentBuilder builder,
394394
Params params,
395395
Throwable throwable,
396-
String type,
397-
String message,
398396
Map<String, List<String>> headers,
399397
Map<String, List<String>> metadata,
400398
Throwable cause,
@@ -408,16 +406,12 @@ protected static void innerToXContent(
408406
return;
409407
}
410408

411-
builder.field(TYPE, type);
412-
builder.field(REASON, message);
409+
builder.field(TYPE, throwable instanceof ElasticsearchException e ? e.getExceptionName() : getExceptionName(throwable));
410+
builder.field(REASON, throwable.getMessage());
413411

414-
boolean timedOut = false;
415-
if (throwable instanceof ElasticsearchException exception) {
416-
// TODO: we could walk the exception chain to see if _any_ causes are timeouts?
417-
timedOut = exception.isTimeout();
418-
}
419-
if (timedOut) {
420-
builder.field(TIMED_OUT, timedOut);
412+
// TODO: we could walk the exception chain to see if _any_ causes are timeouts?
413+
if (throwable instanceof ElasticsearchException exception && exception.isTimeout()) {
414+
builder.field(TIMED_OUT, true);
421415
}
422416

423417
for (Map.Entry<String, List<String>> entry : metadata.entrySet()) {
@@ -428,13 +422,10 @@ protected static void innerToXContent(
428422
exception.metadataToXContent(builder, params);
429423
}
430424

431-
if (params.paramAsBoolean(REST_EXCEPTION_SKIP_CAUSE, REST_EXCEPTION_SKIP_CAUSE_DEFAULT) == false) {
432-
if (cause != null) {
433-
builder.field(CAUSED_BY);
434-
builder.startObject();
435-
generateThrowableXContent(builder, params, cause, nestedLevel + 1);
436-
builder.endObject();
437-
}
425+
if (cause != null && params.paramAsBoolean(REST_EXCEPTION_SKIP_CAUSE, REST_EXCEPTION_SKIP_CAUSE_DEFAULT) == false) {
426+
builder.startObject(CAUSED_BY);
427+
generateThrowableXContent(builder, params, cause, nestedLevel + 1);
428+
builder.endObject();
438429
}
439430

440431
if (headers.isEmpty() == false) {
@@ -607,7 +598,7 @@ public static ElasticsearchException innerFromXContent(XContentParser parser, bo
607598
/**
608599
* Static toXContent helper method that renders {@link org.elasticsearch.ElasticsearchException} or {@link Throwable} instances
609600
* as XContent, delegating the rendering to {@link #toXContent(XContentBuilder, Params)}
610-
* or {@link #innerToXContent(XContentBuilder, Params, Throwable, String, String, Map, Map, Throwable, int)}.
601+
* or {@link #innerToXContent(XContentBuilder, Params, Throwable, Map, Map, Throwable, int)}.
611602
*
612603
* This method is usually used when the {@link Throwable} is rendered as a part of another XContent object, and its result can
613604
* be parsed back using the {@link #fromXContent(XContentParser)} method.
@@ -627,7 +618,7 @@ protected static void generateThrowableXContent(XContentBuilder builder, Params
627618
if (t instanceof ElasticsearchException) {
628619
((ElasticsearchException) t).toXContent(builder, params, nestedLevel);
629620
} else {
630-
innerToXContent(builder, params, t, getExceptionName(t), t.getMessage(), emptyMap(), emptyMap(), t.getCause(), nestedLevel);
621+
innerToXContent(builder, params, t, emptyMap(), emptyMap(), t.getCause(), nestedLevel);
631622
}
632623
}
633624

@@ -723,8 +714,8 @@ public static ElasticsearchException failureFromXContent(XContentParser parser)
723714
*/
724715
public ElasticsearchException[] guessRootCauses() {
725716
final Throwable cause = getCause();
726-
if (cause != null && cause instanceof ElasticsearchException) {
727-
return ((ElasticsearchException) cause).guessRootCauses();
717+
if (cause instanceof ElasticsearchException ese) {
718+
return ese.guessRootCauses();
728719
}
729720
return new ElasticsearchException[] { this };
730721
}
@@ -773,35 +764,28 @@ protected String getExceptionName() {
773764
*/
774765
public static String getExceptionName(Throwable ex) {
775766
String simpleName = ex.getClass().getSimpleName();
776-
if (simpleName.startsWith("Elasticsearch")) {
777-
simpleName = simpleName.substring("Elasticsearch".length());
778-
}
779767
// TODO: do we really need to make the exception name in underscore casing?
780-
return toUnderscoreCase(simpleName);
768+
return toUnderscoreCase(simpleName, simpleName.startsWith("Elasticsearch") ? "Elasticsearch".length() : 0);
781769
}
782770

783771
static String buildMessage(String type, String reason, String stack) {
784-
StringBuilder message = new StringBuilder("Elasticsearch exception [");
785-
message.append(TYPE).append('=').append(type);
786-
message.append(", ").append(REASON).append('=').append(reason);
787-
if (stack != null) {
788-
message.append(", ").append(STACK_TRACE).append('=').append(stack);
789-
}
790-
message.append(']');
791-
return message.toString();
772+
return "Elasticsearch exception ["
773+
+ TYPE
774+
+ "="
775+
+ type
776+
+ ", "
777+
+ REASON
778+
+ "="
779+
+ reason
780+
+ (stack == null ? "" : (", " + STACK_TRACE + "=" + stack))
781+
+ "]";
792782
}
793783

794784
@Override
795785
public String toString() {
796-
StringBuilder builder = new StringBuilder();
797-
if (metadata.containsKey(INDEX_METADATA_KEY)) {
798-
builder.append(getIndex());
799-
if (metadata.containsKey(SHARD_METADATA_KEY)) {
800-
builder.append('[').append(getShardId()).append(']');
801-
}
802-
builder.append(' ');
803-
}
804-
return builder.append(super.toString().trim()).toString();
786+
return (metadata.containsKey(INDEX_METADATA_KEY)
787+
? (getIndex() + (metadata.containsKey(SHARD_METADATA_KEY) ? "[" + getShardId() + "] " : " "))
788+
: "") + super.toString().trim();
805789
}
806790

807791
/**
@@ -2106,19 +2090,17 @@ public String getResourceType() {
21062090
}
21072091

21082092
// lower cases and adds underscores to transitions in a name
2109-
private static String toUnderscoreCase(String value) {
2093+
private static String toUnderscoreCase(String value, final int offset) {
21102094
StringBuilder sb = new StringBuilder();
21112095
boolean changed = false;
2112-
for (int i = 0; i < value.length(); i++) {
2096+
for (int i = offset; i < value.length(); i++) {
21132097
char c = value.charAt(i);
21142098
if (Character.isUpperCase(c)) {
21152099
if (changed == false) {
21162100
// copy it over here
2117-
for (int j = 0; j < i; j++) {
2118-
sb.append(value.charAt(j));
2119-
}
2101+
sb.append(value, offset, i);
21202102
changed = true;
2121-
if (i == 0) {
2103+
if (i == offset) {
21222104
sb.append(Character.toLowerCase(c));
21232105
} else {
21242106
sb.append('_');
@@ -2135,7 +2117,7 @@ private static String toUnderscoreCase(String value) {
21352117
}
21362118
}
21372119
if (changed == false) {
2138-
return value;
2120+
return offset == 0 ? value : value.substring(offset);
21392121
}
21402122
return sb.toString();
21412123
}

server/src/main/java/org/elasticsearch/action/search/SearchPhaseExecutionException.java

+18-28
Original file line numberDiff line numberDiff line change
@@ -106,22 +106,6 @@ public Throwable getCause() {
106106
return cause;
107107
}
108108

109-
private static String buildMessage(String phaseName, String msg, ShardSearchFailure[] shardFailures) {
110-
StringBuilder sb = new StringBuilder();
111-
sb.append("Failed to execute phase [").append(phaseName).append("], ").append(msg);
112-
if (CollectionUtils.isEmpty(shardFailures) == false) {
113-
sb.append("; shardFailures ");
114-
for (ShardSearchFailure shardFailure : shardFailures) {
115-
if (shardFailure.shard() != null) {
116-
sb.append("{").append(shardFailure.shard()).append(": ").append(shardFailure.reason()).append("}");
117-
} else {
118-
sb.append("{").append(shardFailure.reason()).append("}");
119-
}
120-
}
121-
}
122-
return sb.toString();
123-
}
124-
125109
@Override
126110
protected void metadataToXContent(XContentBuilder builder, Params params) throws IOException {
127111
builder.field("phase", phaseName);
@@ -144,17 +128,7 @@ protected XContentBuilder toXContent(XContentBuilder builder, Params params, int
144128
// We don't have a cause when all shards failed, but we do have shards failures so we can "guess" a cause
145129
// (see {@link #getCause()}). Here, we use super.getCause() because we don't want the guessed exception to
146130
// be rendered twice (one in the "cause" field, one in "failed_shards")
147-
innerToXContent(
148-
builder,
149-
params,
150-
this,
151-
getExceptionName(),
152-
getMessage(),
153-
getHeaders(),
154-
getMetadata(),
155-
super.getCause(),
156-
nestedLevel
157-
);
131+
innerToXContent(builder, params, this, getHeaders(), getMetadata(), super.getCause(), nestedLevel);
158132
}
159133
return builder;
160134
}
@@ -172,7 +146,23 @@ public ElasticsearchException[] guessRootCauses() {
172146

173147
@Override
174148
public String toString() {
175-
return buildMessage(phaseName, getMessage(), shardFailures);
149+
return "Failed to execute phase ["
150+
+ phaseName
151+
+ "], "
152+
+ getMessage()
153+
+ (CollectionUtils.isEmpty(shardFailures) ? "" : buildShardFailureString());
154+
}
155+
156+
private String buildShardFailureString() {
157+
StringBuilder sb = new StringBuilder("; shardFailures ");
158+
for (ShardSearchFailure shardFailure : shardFailures) {
159+
sb.append("{");
160+
if (shardFailure.shard() != null) {
161+
sb.append(shardFailure.shard()).append(": ");
162+
}
163+
sb.append(shardFailure.reason()).append("}");
164+
}
165+
return sb.toString();
176166
}
177167

178168
public String getPhaseName() {

0 commit comments

Comments
 (0)