Skip to content

Commit be50439

Browse files
Merge branch 'main' into 2025/06/30/add-node-level-write-load-stats
2 parents 37b20e8 + cdfd3dd commit be50439

File tree

11 files changed

+419
-169
lines changed

11 files changed

+419
-169
lines changed

muted-tests.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,27 @@ tests:
605605
- class: org.elasticsearch.multiproject.test.CoreWithMultipleProjectsClientYamlTestSuiteIT
606606
method: test {yaml=indices.resolve_index/10_basic_resolve_index/Resolve index with hidden and closed indices}
607607
issue: https://github.com/elastic/elasticsearch/issues/130568
608+
- class: org.elasticsearch.reindex.ReindexRestClientSslTests
609+
method: testClientPassesClientCertificate
610+
issue: https://github.com/elastic/elasticsearch/issues/130584
611+
- class: org.elasticsearch.reindex.ReindexRestClientSslTests
612+
method: testClientFailsWithUntrustedCertificate
613+
issue: https://github.com/elastic/elasticsearch/issues/130585
614+
- class: org.elasticsearch.reindex.ReindexRestClientSslTests
615+
method: testClientSucceedsWithCertificateAuthorities
616+
issue: https://github.com/elastic/elasticsearch/issues/130586
617+
- class: org.elasticsearch.reindex.ReindexFromRemoteBuildRestClientTests
618+
method: testHeaders
619+
issue: https://github.com/elastic/elasticsearch/issues/130587
620+
- class: org.elasticsearch.reindex.ReindexFromRemoteBuildRestClientTests
621+
method: testBuildRestClient
622+
issue: https://github.com/elastic/elasticsearch/issues/130588
623+
- class: org.elasticsearch.http.netty4.Netty4HttpServerTransportTests
624+
method: testRespondAfterServiceCloseWithClientCancel
625+
issue: https://github.com/elastic/elasticsearch/issues/130590
626+
- class: org.elasticsearch.http.netty4.Netty4HttpServerTransportTests
627+
method: testRespondAfterServiceCloseWithServerCancel
628+
issue: https://github.com/elastic/elasticsearch/issues/130591
608629

609630
# Examples:
610631
#

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

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
118118
private static final String ROOT_CAUSE = "root_cause";
119119

120120
static final String TIMED_OUT_HEADER = "X-Timed-Out";
121+
static final String EXCEPTION_TYPE_HEADER = "X-Elasticsearch-Exception";
121122

122123
private static final Map<Integer, CheckedFunction<StreamInput, ? extends ElasticsearchException, IOException>> ID_TO_SUPPLIER;
123124
private static final Map<Class<? extends ElasticsearchException>, ElasticsearchExceptionHandle> CLASS_TO_ELASTICSEARCH_EXCEPTION_HANDLE;
@@ -131,7 +132,6 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
131132
@SuppressWarnings("this-escape")
132133
public ElasticsearchException(Throwable cause) {
133134
super(cause);
134-
maybePutTimeoutHeader();
135135
}
136136

137137
/**
@@ -146,7 +146,6 @@ public ElasticsearchException(Throwable cause) {
146146
@SuppressWarnings("this-escape")
147147
public ElasticsearchException(String msg, Object... args) {
148148
super(LoggerMessageFormat.format(msg, args));
149-
maybePutTimeoutHeader();
150149
}
151150

152151
/**
@@ -163,7 +162,6 @@ public ElasticsearchException(String msg, Object... args) {
163162
@SuppressWarnings("this-escape")
164163
public ElasticsearchException(String msg, Throwable cause, Object... args) {
165164
super(LoggerMessageFormat.format(msg, args), cause);
166-
maybePutTimeoutHeader();
167165
}
168166

169167
@SuppressWarnings("this-escape")
@@ -174,11 +172,19 @@ public ElasticsearchException(StreamInput in) throws IOException {
174172
metadata.putAll(in.readMapOfLists(StreamInput::readString));
175173
}
176174

177-
private void maybePutTimeoutHeader() {
175+
private void maybeAddErrorHeaders() {
178176
if (isTimeout()) {
179177
// see https://www.rfc-editor.org/rfc/rfc8941.html#section-4.1.9 for booleans in structured headers
180178
bodyHeaders.put(TIMED_OUT_HEADER, List.of("?1"));
181179
}
180+
if (httpHeaders.containsKey(EXCEPTION_TYPE_HEADER) == false) {
181+
// TODO: cache unwrapping the cause? we do this in several places...
182+
Throwable cause = unwrapCause();
183+
RestStatus status = ExceptionsHelper.status(cause);
184+
if (status.getStatus() >= 500) {
185+
httpHeaders.put(EXCEPTION_TYPE_HEADER, List.of(cause.getClass().getSimpleName()));
186+
}
187+
}
182188
}
183189

184190
/**
@@ -244,6 +250,7 @@ public void addBodyHeader(String key, String... value) {
244250
* Returns a set of all body header keys on this exception
245251
*/
246252
public Set<String> getBodyHeaderKeys() {
253+
maybeAddErrorHeaders();
247254
return bodyHeaders.keySet();
248255
}
249256

@@ -252,10 +259,12 @@ public Set<String> getBodyHeaderKeys() {
252259
* given key exists.
253260
*/
254261
public List<String> getBodyHeader(String key) {
262+
maybeAddErrorHeaders();
255263
return bodyHeaders.get(key);
256264
}
257265

258266
protected Map<String, List<String>> getBodyHeaders() {
267+
maybeAddErrorHeaders();
259268
return bodyHeaders;
260269
}
261270

@@ -279,6 +288,7 @@ public void addHttpHeader(String key, String... value) {
279288
* Returns a set of all body header keys on this exception
280289
*/
281290
public Set<String> getHttpHeaderKeys() {
291+
maybeAddErrorHeaders();
282292
return httpHeaders.keySet();
283293
}
284294

@@ -287,10 +297,12 @@ public Set<String> getHttpHeaderKeys() {
287297
* given key exists.
288298
*/
289299
public List<String> getHttpHeader(String key) {
300+
maybeAddErrorHeaders();
290301
return httpHeaders.get(key);
291302
}
292303

293304
protected Map<String, List<String>> getHttpHeaders() {
305+
maybeAddErrorHeaders();
294306
return httpHeaders;
295307
}
296308

server/src/main/java/org/elasticsearch/index/mapper/vectors/SparseVectorFieldMapper.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -369,9 +369,9 @@ public void parse(DocumentParserContext context) throws IOException {
369369
// based on recommendations from this paper: https://arxiv.org/pdf/2305.18494.pdf
370370
IndexableField currentField = context.doc().getByKey(key);
371371
if (currentField == null) {
372-
context.doc().addWithKey(key, new XFeatureField(fullPath(), feature, value, fieldType().isStored()));
373-
} else if (currentField instanceof XFeatureField && ((XFeatureField) currentField).getFeatureValue() < value) {
374-
((XFeatureField) currentField).setFeatureValue(value);
372+
context.doc().addWithKey(key, new FeatureField(fullPath(), feature, value, fieldType().isStored()));
373+
} else if (currentField instanceof FeatureField ff && ff.getFeatureValue() < value) {
374+
ff.setFeatureValue(value);
375375
}
376376
} else {
377377
throw new IllegalArgumentException(

server/src/main/java/org/elasticsearch/index/mapper/vectors/XFeatureField.java

Lines changed: 2 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -18,149 +18,13 @@
1818

1919
package org.elasticsearch.index.mapper.vectors;
2020

21-
import org.apache.lucene.analysis.Analyzer;
22-
import org.apache.lucene.analysis.TokenStream;
23-
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
24-
import org.apache.lucene.analysis.tokenattributes.TermFrequencyAttribute;
2521
import org.apache.lucene.document.FeatureField;
26-
import org.apache.lucene.document.Field;
27-
import org.apache.lucene.document.FieldType;
28-
import org.apache.lucene.index.IndexOptions;
2922

3023
/**
3124
* This class is forked from the Lucene {@link FeatureField} implementation to enable support for storing term vectors.
32-
* It should be removed once apache/lucene#14034 becomes available.
25+
* Its purpose is to allow decoding the feature value from the term frequency
3326
*/
34-
public final class XFeatureField extends Field {
35-
private static final FieldType FIELD_TYPE = new FieldType();
36-
private static final FieldType FIELD_TYPE_STORE_TERM_VECTORS = new FieldType();
37-
38-
static {
39-
FIELD_TYPE.setTokenized(false);
40-
FIELD_TYPE.setOmitNorms(true);
41-
FIELD_TYPE.setIndexOptions(IndexOptions.DOCS_AND_FREQS);
42-
43-
FIELD_TYPE_STORE_TERM_VECTORS.setTokenized(false);
44-
FIELD_TYPE_STORE_TERM_VECTORS.setOmitNorms(true);
45-
FIELD_TYPE_STORE_TERM_VECTORS.setIndexOptions(IndexOptions.DOCS_AND_FREQS);
46-
FIELD_TYPE_STORE_TERM_VECTORS.setStoreTermVectors(true);
47-
}
48-
49-
private float featureValue;
50-
51-
/**
52-
* Create a feature.
53-
*
54-
* @param fieldName The name of the field to store the information into. All features may be
55-
* stored in the same field.
56-
* @param featureName The name of the feature, eg. 'pagerank`. It will be indexed as a term.
57-
* @param featureValue The value of the feature, must be a positive, finite, normal float.
58-
*/
59-
public XFeatureField(String fieldName, String featureName, float featureValue) {
60-
this(fieldName, featureName, featureValue, false);
61-
}
62-
63-
/**
64-
* Create a feature.
65-
*
66-
* @param fieldName The name of the field to store the information into. All features may be
67-
* stored in the same field.
68-
* @param featureName The name of the feature, eg. 'pagerank`. It will be indexed as a term.
69-
* @param featureValue The value of the feature, must be a positive, finite, normal float.
70-
*/
71-
public XFeatureField(String fieldName, String featureName, float featureValue, boolean storeTermVectors) {
72-
super(fieldName, featureName, storeTermVectors ? FIELD_TYPE_STORE_TERM_VECTORS : FIELD_TYPE);
73-
setFeatureValue(featureValue);
74-
}
75-
76-
/**
77-
* Update the feature value of this field.
78-
*/
79-
public void setFeatureValue(float featureValue) {
80-
if (Float.isFinite(featureValue) == false) {
81-
throw new IllegalArgumentException(
82-
"featureValue must be finite, got: " + featureValue + " for feature " + fieldsData + " on field " + name
83-
);
84-
}
85-
if (featureValue < Float.MIN_NORMAL) {
86-
throw new IllegalArgumentException(
87-
"featureValue must be a positive normal float, got: "
88-
+ featureValue
89-
+ " for feature "
90-
+ fieldsData
91-
+ " on field "
92-
+ name
93-
+ " which is less than the minimum positive normal float: "
94-
+ Float.MIN_NORMAL
95-
);
96-
}
97-
this.featureValue = featureValue;
98-
}
99-
100-
@Override
101-
public TokenStream tokenStream(Analyzer analyzer, TokenStream reuse) {
102-
FeatureTokenStream stream;
103-
if (reuse instanceof FeatureTokenStream) {
104-
stream = (FeatureTokenStream) reuse;
105-
} else {
106-
stream = new FeatureTokenStream();
107-
}
108-
109-
int freqBits = Float.floatToIntBits(featureValue);
110-
stream.setValues((String) fieldsData, freqBits >>> 15);
111-
return stream;
112-
}
113-
114-
/**
115-
* This is useful if you have multiple features sharing a name and you want to take action to
116-
* deduplicate them.
117-
*
118-
* @return the feature value of this field.
119-
*/
120-
public float getFeatureValue() {
121-
return featureValue;
122-
}
123-
124-
private static final class FeatureTokenStream extends TokenStream {
125-
private final CharTermAttribute termAttribute = addAttribute(CharTermAttribute.class);
126-
private final TermFrequencyAttribute freqAttribute = addAttribute(TermFrequencyAttribute.class);
127-
private boolean used = true;
128-
private String value = null;
129-
private int freq = 0;
130-
131-
private FeatureTokenStream() {}
132-
133-
/**
134-
* Sets the values
135-
*/
136-
void setValues(String value, int freq) {
137-
this.value = value;
138-
this.freq = freq;
139-
}
140-
141-
@Override
142-
public boolean incrementToken() {
143-
if (used) {
144-
return false;
145-
}
146-
clearAttributes();
147-
termAttribute.append(value);
148-
freqAttribute.setTermFrequency(freq);
149-
used = true;
150-
return true;
151-
}
152-
153-
@Override
154-
public void reset() {
155-
used = false;
156-
}
157-
158-
@Override
159-
public void close() {
160-
value = null;
161-
}
162-
}
163-
27+
public final class XFeatureField {
16428
static final int MAX_FREQ = Float.floatToIntBits(Float.MAX_VALUE) >>> 15;
16529

16630
static float decodeFeatureValue(float freq) {

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

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1553,19 +1553,24 @@ private void testExceptionLoop(Exception rootException) throws IOException {
15531553
assertArrayEquals(ser.getStackTrace(), rootException.getStackTrace());
15541554
}
15551555

1556-
static class ExceptionSubclass extends ElasticsearchException {
1556+
static class TimeoutSubclass extends ElasticsearchException {
1557+
TimeoutSubclass(String message) {
1558+
super(message);
1559+
}
1560+
15571561
@Override
15581562
public boolean isTimeout() {
15591563
return true;
15601564
}
15611565

1562-
ExceptionSubclass(String message) {
1563-
super(message);
1566+
@Override
1567+
public RestStatus status() {
1568+
return RestStatus.BAD_REQUEST;
15641569
}
15651570
}
15661571

1567-
public void testTimeout() throws IOException {
1568-
var e = new ExceptionSubclass("some timeout");
1572+
public void testTimeoutHeader() throws IOException {
1573+
var e = new TimeoutSubclass("some timeout");
15691574
assertThat(e.getBodyHeaderKeys(), hasItem(ElasticsearchException.TIMED_OUT_HEADER));
15701575

15711576
XContentBuilder builder = XContentFactory.jsonBuilder();
@@ -1574,7 +1579,7 @@ public void testTimeout() throws IOException {
15741579
builder.endObject();
15751580
String expected = """
15761581
{
1577-
"type": "exception_subclass",
1582+
"type": "timeout_subclass",
15781583
"reason": "some timeout",
15791584
"timed_out": true,
15801585
"header": {
@@ -1584,6 +1589,44 @@ public void testTimeout() throws IOException {
15841589
assertEquals(XContentHelper.stripWhitespace(expected), Strings.toString(builder));
15851590
}
15861591

1592+
static class Exception5xx extends ElasticsearchException {
1593+
Exception5xx(String message) {
1594+
super(message);
1595+
}
1596+
1597+
@Override
1598+
public RestStatus status() {
1599+
return RestStatus.INTERNAL_SERVER_ERROR;
1600+
}
1601+
}
1602+
1603+
static class Exception4xx extends ElasticsearchException {
1604+
Exception4xx(String message) {
1605+
super(message);
1606+
}
1607+
1608+
@Override
1609+
public RestStatus status() {
1610+
return RestStatus.BAD_REQUEST;
1611+
}
1612+
}
1613+
1614+
public void testExceptionTypeHeader() throws IOException {
1615+
var e = new Exception5xx("some exception");
1616+
assertThat(e.getHttpHeaderKeys(), hasItem(ElasticsearchException.EXCEPTION_TYPE_HEADER));
1617+
1618+
XContentBuilder builder = XContentFactory.jsonBuilder();
1619+
builder.startObject();
1620+
e.toXContent(builder, ToXContent.EMPTY_PARAMS);
1621+
builder.endObject();
1622+
String expected = """
1623+
{
1624+
"type": "exception5xx",
1625+
"reason": "some exception"
1626+
}""";
1627+
assertEquals(XContentHelper.stripWhitespace(expected), Strings.toString(builder));
1628+
}
1629+
15871630
public void testHttpHeaders() throws IOException {
15881631
var e = new ElasticsearchException("some exception");
15891632
e.addHttpHeader("My-Header", "value");
@@ -1592,13 +1635,19 @@ public void testHttpHeaders() throws IOException {
15921635
assertThat(e.getHttpHeaders(), hasEntry("My-Header", List.of("value")));
15931636

15941637
// ensure http headers are not written to response body
1638+
}
1639+
1640+
public void testNoExceptionTypeHeaderOn4xx() throws IOException {
1641+
var e = new Exception4xx("some exception");
1642+
assertThat(e.getHttpHeaderKeys(), not(hasItem(ElasticsearchException.EXCEPTION_TYPE_HEADER)));
1643+
15951644
XContentBuilder builder = XContentFactory.jsonBuilder();
15961645
builder.startObject();
15971646
e.toXContent(builder, ToXContent.EMPTY_PARAMS);
15981647
builder.endObject();
15991648
String expected = """
16001649
{
1601-
"type": "exception",
1650+
"type": "exception4xx",
16021651
"reason": "some exception"
16031652
}""";
16041653
assertEquals(XContentHelper.stripWhitespace(expected), Strings.toString(builder));

0 commit comments

Comments
 (0)