Skip to content

Commit 678b7e5

Browse files
committed
Merge remote-tracking branch 'origin/main' into engine-shard-field-stats
2 parents 51547bf + 9866a67 commit 678b7e5

File tree

11 files changed

+138
-8
lines changed

11 files changed

+138
-8
lines changed

docs/changelog/124936.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 124936
2+
summary: Indicate when errors represent timeouts
3+
area: Infra/REST API
4+
type: enhancement
5+
issues: []

muted-tests.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,15 @@ tests:
399399
- class: org.elasticsearch.packaging.test.DockerTests
400400
method: test010Install
401401
issue: https://github.com/elastic/elasticsearch/issues/119441
402+
- class: org.elasticsearch.xpack.inference.external.action.azureopenai.AzureOpenAiEmbeddingsActionTests
403+
method: testExecute_ReturnsSuccessfulResponse
404+
issue: https://github.com/elastic/elasticsearch/issues/125057
405+
- class: org.elasticsearch.xpack.inference.external.request.jinaai.JinaAIEmbeddingsRequestTests
406+
method: testCreateRequest_TaskSettingsInputType
407+
issue: https://github.com/elastic/elasticsearch/issues/125059
408+
- class: org.elasticsearch.xpack.inference.external.request.azureopenai.embeddings.AzureOpenAiEmbeddingsRequestTests
409+
method: testCreateRequest_WithEntraIdDefined
410+
issue: https://github.com/elastic/elasticsearch/issues/125061
402411

403412
# Examples:
404413
#

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,16 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
108108

109109
private static final String TYPE = "type";
110110
private static final String REASON = "reason";
111+
private static final String TIMED_OUT = "timed_out";
111112
private static final String CAUSED_BY = "caused_by";
112113
private static final ParseField SUPPRESSED = new ParseField("suppressed");
113114
public static final String STACK_TRACE = "stack_trace";
114115
private static final String HEADER = "header";
115116
private static final String ERROR = "error";
116117
private static final String ROOT_CAUSE = "root_cause";
117118

119+
static final String TIMED_OUT_HEADER = "X-Timed-Out";
120+
118121
private static final Map<Integer, CheckedFunction<StreamInput, ? extends ElasticsearchException, IOException>> ID_TO_SUPPLIER;
119122
private static final Map<Class<? extends ElasticsearchException>, ElasticsearchExceptionHandle> CLASS_TO_ELASTICSEARCH_EXCEPTION_HANDLE;
120123
private final Map<String, List<String>> metadata = new HashMap<>();
@@ -123,8 +126,10 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
123126
/**
124127
* Construct a <code>ElasticsearchException</code> with the specified cause exception.
125128
*/
129+
@SuppressWarnings("this-escape")
126130
public ElasticsearchException(Throwable cause) {
127131
super(cause);
132+
maybePutTimeoutHeader();
128133
}
129134

130135
/**
@@ -136,8 +141,10 @@ public ElasticsearchException(Throwable cause) {
136141
* @param msg the detail message
137142
* @param args the arguments for the message
138143
*/
144+
@SuppressWarnings("this-escape")
139145
public ElasticsearchException(String msg, Object... args) {
140146
super(LoggerMessageFormat.format(msg, args));
147+
maybePutTimeoutHeader();
141148
}
142149

143150
/**
@@ -151,8 +158,10 @@ public ElasticsearchException(String msg, Object... args) {
151158
* @param cause the nested exception
152159
* @param args the arguments for the message
153160
*/
161+
@SuppressWarnings("this-escape")
154162
public ElasticsearchException(String msg, Throwable cause, Object... args) {
155163
super(LoggerMessageFormat.format(msg, args), cause);
164+
maybePutTimeoutHeader();
156165
}
157166

158167
@SuppressWarnings("this-escape")
@@ -163,6 +172,13 @@ public ElasticsearchException(StreamInput in) throws IOException {
163172
metadata.putAll(in.readMapOfLists(StreamInput::readString));
164173
}
165174

175+
private void maybePutTimeoutHeader() {
176+
if (isTimeout()) {
177+
// see https://www.rfc-editor.org/rfc/rfc8941.html#section-4.1.9 for booleans in structured headers
178+
headers.put(TIMED_OUT_HEADER, List.of("?1"));
179+
}
180+
}
181+
166182
/**
167183
* Adds a new piece of metadata with the given key.
168184
* If the provided key is already present, the corresponding metadata will be replaced
@@ -253,6 +269,13 @@ public RestStatus status() {
253269
}
254270
}
255271

272+
/**
273+
* Returns whether this exception represents a timeout.
274+
*/
275+
public boolean isTimeout() {
276+
return false;
277+
}
278+
256279
/**
257280
* Unwraps the actual cause from the exception for cases when the exception is a
258281
* {@link ElasticsearchWrapperException}.
@@ -386,6 +409,15 @@ protected static void innerToXContent(
386409
builder.field(TYPE, type);
387410
builder.field(REASON, message);
388411

412+
boolean timedOut = false;
413+
if (throwable instanceof ElasticsearchException exception) {
414+
// TODO: we could walk the exception chain to see if _any_ causes are timeouts?
415+
timedOut = exception.isTimeout();
416+
}
417+
if (timedOut) {
418+
builder.field(TIMED_OUT, timedOut);
419+
}
420+
389421
for (Map.Entry<String, List<String>> entry : metadata.entrySet()) {
390422
headerToXContent(builder, entry.getKey().substring("es.".length()), entry.getValue());
391423
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,9 @@ public RestStatus status() {
4141
// closest thing to "your request took longer than you asked for"
4242
return RestStatus.TOO_MANY_REQUESTS;
4343
}
44+
45+
@Override
46+
public boolean isTimeout() {
47+
return true;
48+
}
4449
}

server/src/main/java/org/elasticsearch/cluster/metadata/IndexReshardingMetadata.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,10 +205,27 @@ public static IndexReshardingMetadata newSplitByMultiple(int shardCount, int mul
205205
return new IndexReshardingMetadata(IndexReshardingState.Split.newSplitByMultiple(shardCount, multiple));
206206
}
207207

208+
/**
209+
* @return the split state of this metadata block, or throw IllegalArgumentException if this metadata doesn't represent a split
210+
*/
208211
public IndexReshardingState.Split getSplit() {
209212
return switch (state) {
210213
case IndexReshardingState.Noop ignored -> throw new IllegalArgumentException("resharding metadata is not a split");
211214
case IndexReshardingState.Split s -> s;
212215
};
213216
}
217+
218+
/**
219+
* @return the number of shards the index has at the start of this operation
220+
*/
221+
public int shardCountBefore() {
222+
return state.shardCountBefore();
223+
}
224+
225+
/**
226+
* @return the number of shards that the index will have when resharding completes
227+
*/
228+
public int shardCountAfter() {
229+
return state.shardCountAfter();
230+
}
214231
}

server/src/main/java/org/elasticsearch/cluster/metadata/IndexReshardingState.java

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@
2929
* concrete subclasses for the operations that are currently defined (which is only split for now).
3030
*/
3131
public abstract sealed class IndexReshardingState implements Writeable, ToXContentFragment {
32+
/**
33+
* @return the number of shards the index has at the start of this operation
34+
*/
35+
public abstract int shardCountBefore();
36+
37+
/**
38+
* @return the number of shards that the index will have when resharding completes
39+
*/
40+
public abstract int shardCountAfter();
41+
3242
// This class exists only so that tests can check that IndexReshardingMetadata can support more than one kind of operation.
3343
// When we have another real operation such as Shrink this can be removed.
3444
public static final class Noop extends IndexReshardingState {
@@ -64,6 +74,16 @@ public boolean equals(Object other) {
6474
public int hashCode() {
6575
return 0;
6676
}
77+
78+
@Override
79+
public int shardCountBefore() {
80+
return 1;
81+
}
82+
83+
@Override
84+
public int shardCountAfter() {
85+
return 1;
86+
}
6787
}
6888

6989
public static final class Split extends IndexReshardingState {
@@ -210,13 +230,13 @@ public int hashCode() {
210230
return Objects.hash(Arrays.hashCode(sourceShards), Arrays.hashCode(targetShards));
211231
}
212232

213-
// visible for testing
214-
int oldShardCount() {
233+
@Override
234+
public int shardCountBefore() {
215235
return oldShardCount;
216236
}
217237

218-
// visible for testing
219-
int newShardCount() {
238+
@Override
239+
public int shardCountAfter() {
220240
return newShardCount;
221241
}
222242

server/src/main/java/org/elasticsearch/cluster/metadata/ProcessClusterEventTimeoutException.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,9 @@ public ProcessClusterEventTimeoutException(StreamInput in) throws IOException {
3030
public RestStatus status() {
3131
return RestStatus.TOO_MANY_REQUESTS;
3232
}
33+
34+
@Override
35+
public boolean isTimeout() {
36+
return true;
37+
}
3338
}

server/src/main/java/org/elasticsearch/search/query/SearchTimeoutException.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
/**
2020
* Specific instance of {@link SearchException} that indicates that a search timeout occurred.
21-
* Always returns http status 504 (Gateway Timeout)
21+
* Always returns http status 429 (Too Many Requests)
2222
*/
2323
public class SearchTimeoutException extends SearchException {
2424
public SearchTimeoutException(SearchShardTarget shardTarget, String msg) {
@@ -34,6 +34,11 @@ public RestStatus status() {
3434
return RestStatus.TOO_MANY_REQUESTS;
3535
}
3636

37+
@Override
38+
public boolean isTimeout() {
39+
return true;
40+
}
41+
3742
/**
3843
* Propagate a timeout according to whether partial search results are allowed or not.
3944
* In case partial results are allowed, a flag will be set to the provided {@link QuerySearchResult} to indicate that there was a

server/src/test/java/org/elasticsearch/ElasticsearchExceptionTests.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1552,4 +1552,35 @@ private void testExceptionLoop(Exception rootException) throws IOException {
15521552
assertThat(ser.getMessage(), equalTo(rootException.getMessage()));
15531553
assertArrayEquals(ser.getStackTrace(), rootException.getStackTrace());
15541554
}
1555+
1556+
static class ExceptionSubclass extends ElasticsearchException {
1557+
@Override
1558+
public boolean isTimeout() {
1559+
return true;
1560+
}
1561+
1562+
ExceptionSubclass(String message) {
1563+
super(message);
1564+
}
1565+
}
1566+
1567+
public void testTimeout() throws IOException {
1568+
var e = new ExceptionSubclass("some timeout");
1569+
assertThat(e.getHeaderKeys(), hasItem(ElasticsearchException.TIMED_OUT_HEADER));
1570+
1571+
XContentBuilder builder = XContentFactory.jsonBuilder();
1572+
builder.startObject();
1573+
e.toXContent(builder, ToXContent.EMPTY_PARAMS);
1574+
builder.endObject();
1575+
String expected = """
1576+
{
1577+
"type": "exception_subclass",
1578+
"reason": "some timeout",
1579+
"timed_out": true,
1580+
"header": {
1581+
"X-Timed-Out": "?1"
1582+
}
1583+
}""";
1584+
assertEquals(XContentHelper.stripWhitespace(expected), Strings.toString(builder));
1585+
}
15551586
}

server/src/test/java/org/elasticsearch/ExceptionSerializationTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,8 @@ public void testExceptionRegistration() throws IOException, URISyntaxException {
146146
final Set<? extends Class<?>> ignore = Sets.newHashSet(
147147
CancellableThreadsTests.CustomException.class,
148148
RestResponseTests.WithHeadersException.class,
149-
AbstractClientHeadersTestCase.InternalException.class
149+
AbstractClientHeadersTestCase.InternalException.class,
150+
ElasticsearchExceptionTests.ExceptionSubclass.class
150151
);
151152
FileVisitor<Path> visitor = new FileVisitor<Path>() {
152153
private Path pkgPrefix = PathUtils.get(path).getParent();

0 commit comments

Comments
 (0)