Skip to content

Commit 348305f

Browse files
Merge remote-tracking branch 'upstream/main' into knn-as-query
2 parents e0ae1dc + bfffbd4 commit 348305f

File tree

21 files changed

+157
-61
lines changed

21 files changed

+157
-61
lines changed

docs/changelog/100368.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 100368
2+
summary: "Status codes for Aggregation errors, part 2"
3+
area: Aggregations
4+
type: enhancement
5+
issues: []

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.elasticsearch.rest.RestStatus;
3434
import org.elasticsearch.search.SearchException;
3535
import org.elasticsearch.search.TooManyScrollContextsException;
36+
import org.elasticsearch.search.aggregations.AggregationExecutionException;
3637
import org.elasticsearch.search.aggregations.MultiBucketConsumerService;
3738
import org.elasticsearch.search.aggregations.UnsupportedAggregationOnDownsampledIndex;
3839
import org.elasticsearch.transport.TcpTransport;
@@ -1861,6 +1862,12 @@ private enum ElasticsearchExceptionHandle {
18611862
TooManyScrollContextsException::new,
18621863
173,
18631864
TransportVersions.TOO_MANY_SCROLL_CONTEXTS_EXCEPTION_ADDED
1865+
),
1866+
INVALID_BUCKET_PATH_EXCEPTION(
1867+
AggregationExecutionException.InvalidPath.class,
1868+
AggregationExecutionException.InvalidPath::new,
1869+
174,
1870+
TransportVersions.INVALID_BUCKET_PATH_EXCEPTION_INTRODUCED
18641871
);
18651872

18661873
final Class<? extends ElasticsearchException> exceptionClass;

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@
2020
import java.util.TreeMap;
2121
import java.util.TreeSet;
2222

23+
/**
24+
* <p>Transport version is used to coordinate compatible wire protocol communication between nodes, at a fine-grained level. This replaces
25+
* and supersedes the old Version constants.</p>
26+
*
27+
* <p>Before adding a new version constant, please read the block comment at the end of the list of constants.</p>
28+
*/
2329
public class TransportVersions {
2430

2531
/*
@@ -150,7 +156,8 @@ static TransportVersion def(int id) {
150156
public static final TransportVersion PRIMARY_TERM_ADDED = def(8_525_00_0);
151157
public static final TransportVersion CLUSTER_FEATURES_ADDED = def(8_526_00_0);
152158
public static final TransportVersion DSL_ERROR_STORE_INFORMATION_ENHANCED = def(8_527_00_0);
153-
public static final TransportVersion KNN_AS_QUERY_ADDED = def(8_528_00_0);
159+
public static final TransportVersion INVALID_BUCKET_PATH_EXCEPTION_INTRODUCED = def(8_528_00_0);
160+
public static final TransportVersion KNN_AS_QUERY_ADDED = def(8_529_00_0);
154161

155162
/*
156163
* STOP! READ THIS FIRST! No, really,

server/src/main/java/org/elasticsearch/search/aggregations/AggregationErrors.java

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
* Collection of helper methods for what to throw in common aggregation error scenarios.
1717
*/
1818
public class AggregationErrors {
19-
private static String parentType;
2019

2120
private AggregationErrors() {}
2221

@@ -68,7 +67,7 @@ public static RuntimeException rateWithoutDateHistogram(String name) {
6867
* @param position - optional, for multisource aggregations. Indicates the position of the field causing the problem.
6968
* @return - an appropriate exception
7069
*/
71-
public static RuntimeException reduceTypeMissmatch(String aggregationName, Optional<Integer> position) {
70+
public static RuntimeException reduceTypeMismatch(String aggregationName, Optional<Integer> position) {
7271
String fieldString;
7372
if (position.isPresent()) {
7473
fieldString = "the field in position" + position.get().toString();
@@ -104,6 +103,15 @@ public static RuntimeException unsupportedMultivalue() {
104103
);
105104
}
106105

106+
/**
107+
* Indicates the given values source is not suitable for use in a multivalued aggregation. This is not retryable.
108+
* @param source a string describing the Values Source
109+
* @return an appropriate exception
110+
*/
111+
public static RuntimeException unsupportedMultivalueValuesSource(String source) {
112+
throw new IllegalArgumentException("ValuesSource type " + source + "is not supported for multi-valued aggregation");
113+
}
114+
107115
/**
108116
* Indicates an attempt to use date rounding on a non-date values source
109117
* @param typeName - name of the type we're attempting to round
@@ -112,4 +120,41 @@ public static RuntimeException unsupportedMultivalue() {
112120
public static RuntimeException unsupportedRounding(String typeName) {
113121
return new IllegalArgumentException("can't round a [" + typeName + "]");
114122
}
123+
124+
/**
125+
* Indicates that an aggregation path (e.g. from a pipeline agg) references an aggregation of the wrong type, for example
126+
* attempting to take a cumulative cardinality of something other than a cardinality aggregation.
127+
*
128+
* @param aggPath the path element found to be invalid
129+
* @param expected
130+
* @param got What we actually got; this may be null.
131+
* @param currentAgg The name of the aggregation in question
132+
* @return an appropriate exception
133+
*/
134+
public static RuntimeException incompatibleAggregationType(String aggPath, String expected, String got, String currentAgg) {
135+
136+
return new AggregationExecutionException.InvalidPath(
137+
aggPath
138+
+ " must reference a "
139+
+ expected
140+
+ " aggregation, got: ["
141+
+ (got == null ? "null" : got)
142+
+ "] at aggregation ["
143+
+ currentAgg
144+
+ "]"
145+
);
146+
}
147+
148+
/**
149+
* This is a 500 class error indicating a programming error. Hopefully we never see this outside of tests.
150+
* @param bucketOrds - the ords we are processing
151+
* @param got - the ordinal we got
152+
* @param expected - the ordinal we expected
153+
* @return an appropriate exception
154+
*/
155+
public static RuntimeException iterationOrderChangedWithoutMutating(String bucketOrds, long got, long expected) {
156+
return new AggregationExecutionException(
157+
"Iteration order of [" + bucketOrds + "] changed without mutating. [" + got + "] should have been [" + expected + "]"
158+
);
159+
}
115160
}

server/src/main/java/org/elasticsearch/search/aggregations/AggregationExecutionException.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import org.elasticsearch.ElasticsearchException;
1111
import org.elasticsearch.common.io.stream.StreamInput;
12+
import org.elasticsearch.rest.RestStatus;
1213

1314
import java.io.IOException;
1415

@@ -28,4 +29,24 @@ public AggregationExecutionException(String msg, Throwable cause) {
2829
public AggregationExecutionException(StreamInput in) throws IOException {
2930
super(in);
3031
}
32+
33+
public static class InvalidPath extends AggregationExecutionException {
34+
35+
public InvalidPath(String msg) {
36+
super(msg);
37+
}
38+
39+
public InvalidPath(String msg, Throwable cause) {
40+
super(msg, cause);
41+
}
42+
43+
public InvalidPath(StreamInput in) throws IOException {
44+
super(in);
45+
}
46+
47+
@Override
48+
public RestStatus status() {
49+
return RestStatus.BAD_REQUEST;
50+
}
51+
}
3152
}

server/src/main/java/org/elasticsearch/search/aggregations/AggregationPhase.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ private static void executeInSortOrder(SearchContext context, BucketCollector co
6464
try {
6565
searcher.search(context.rewrittenQuery(), collector);
6666
} catch (IOException e) {
67+
// Seems like this should be 400 (non-retryable), but we clearly intentionally throw a 500 here. Why?
6768
throw new AggregationExecutionException("Could not perform time series aggregation", e);
6869
}
6970
}

server/src/main/java/org/elasticsearch/search/aggregations/InternalOrder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public <T extends Bucket> Comparator<T> partiallyBuiltBucketComparator(ToLongFun
6565
BucketComparator bucketComparator = path.bucketComparator(aggregator, order);
6666
return (lhs, rhs) -> bucketComparator.compare(ordinalReader.applyAsLong(lhs), ordinalReader.applyAsLong(rhs));
6767
} catch (IllegalArgumentException e) {
68-
throw new AggregationExecutionException("Invalid aggregation order path [" + path + "]. " + e.getMessage(), e);
68+
throw new AggregationExecutionException.InvalidPath("Invalid aggregation order path [" + path + "]. " + e.getMessage(), e);
6969
}
7070
}
7171

server/src/main/java/org/elasticsearch/search/aggregations/bucket/BucketsAggregator.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import org.elasticsearch.common.breaker.CircuitBreaker;
1212
import org.elasticsearch.common.util.LongArray;
1313
import org.elasticsearch.core.Releasable;
14-
import org.elasticsearch.search.aggregations.AggregationExecutionException;
14+
import org.elasticsearch.search.aggregations.AggregationErrors;
1515
import org.elasticsearch.search.aggregations.Aggregator;
1616
import org.elasticsearch.search.aggregations.AggregatorBase;
1717
import org.elasticsearch.search.aggregations.AggregatorFactories;
@@ -340,7 +340,9 @@ protected final <B> InternalAggregation[] buildAggregationsForVariableBuckets(
340340
totalOrdsToCollect += bucketCount;
341341
}
342342
if (totalOrdsToCollect > Integer.MAX_VALUE) {
343-
throw new AggregationExecutionException(
343+
// TODO: We should instrument this error. While it is correct for it to be a 400 class IllegalArgumentException, there is not
344+
// much the user can do about that. If this occurs with any frequency, we should do something about it.
345+
throw new IllegalArgumentException(
344346
"Can't collect more than [" + Integer.MAX_VALUE + "] buckets but attempted [" + totalOrdsToCollect + "]"
345347
);
346348
}
@@ -361,14 +363,11 @@ protected final <B> InternalAggregation[] buildAggregationsForVariableBuckets(
361363
LongKeyedBucketOrds.BucketOrdsEnum ordsEnum = bucketOrds.ordsEnum(owningBucketOrds[ordIdx]);
362364
while (ordsEnum.next()) {
363365
if (bucketOrdsToCollect[b] != ordsEnum.ord()) {
364-
throw new AggregationExecutionException(
365-
"Iteration order of ["
366-
+ bucketOrds
367-
+ "] changed without mutating. ["
368-
+ ordsEnum.ord()
369-
+ "] should have been ["
370-
+ bucketOrdsToCollect[b]
371-
+ "]"
366+
// If we hit this, something has gone horribly wrong and we need to investigate
367+
throw AggregationErrors.iterationOrderChangedWithoutMutating(
368+
bucketOrds.toString(),
369+
ordsEnum.ord(),
370+
bucketOrdsToCollect[b]
372371
);
373372
}
374373
buckets.add(bucketBuilder.build(ordsEnum.value(), bucketDocCount(ordsEnum.ord()), subAggregationResults[b++]));

server/src/main/java/org/elasticsearch/search/aggregations/bucket/global/GlobalAggregatorFactory.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public Aggregator createInternal(Aggregator parent, CardinalityUpperBound cardin
4747
);
4848
}
4949
if (cardinality != CardinalityUpperBound.ONE) {
50+
// Hitting this exception is a programmer error. Hopefully never seen in production.
5051
throw new AggregationExecutionException("Aggregation [" + name() + "] must have cardinality 1 but was [" + cardinality + "]");
5152
}
5253
return new GlobalAggregator(name, factories, context, metadata);

server/src/main/java/org/elasticsearch/search/aggregations/bucket/prefix/IpPrefixAggregator.java

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
import org.apache.lucene.util.CollectionUtil;
1313
import org.elasticsearch.core.Releasables;
1414
import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
15+
import org.elasticsearch.search.aggregations.AggregationErrors;
1516
import org.elasticsearch.search.aggregations.AggregationExecutionContext;
16-
import org.elasticsearch.search.aggregations.AggregationExecutionException;
1717
import org.elasticsearch.search.aggregations.Aggregator;
1818
import org.elasticsearch.search.aggregations.AggregatorFactories;
1919
import org.elasticsearch.search.aggregations.BucketOrder;
@@ -195,15 +195,7 @@ public InternalAggregation[] buildAggregations(long[] owningBucketOrds) throws I
195195
while (ordsEnum.next()) {
196196
long ordinal = ordsEnum.ord();
197197
if (bucketOrdsToCollect[b] != ordinal) {
198-
throw new AggregationExecutionException(
199-
"Iteration order of ["
200-
+ bucketOrds
201-
+ "] changed without mutating. ["
202-
+ ordinal
203-
+ "] should have been ["
204-
+ bucketOrdsToCollect[b]
205-
+ "]"
206-
);
198+
throw AggregationErrors.iterationOrderChangedWithoutMutating(bucketOrds.toString(), ordinal, bucketOrdsToCollect[b]);
207199
}
208200
BytesRef ipAddress = new BytesRef();
209201
ordsEnum.readValue(ipAddress);

server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/AbstractInternalTerms.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ public InternalAggregation reduce(List<InternalAggregation> aggregations, Aggreg
270270
if (referenceTerms != null && referenceTerms.getClass().equals(terms.getClass()) == false && terms.canLeadReduction()) {
271271
// control gets into this loop when the same field name against which the query is executed
272272
// is of different types in different indices.
273-
throw AggregationErrors.reduceTypeMissmatch(referenceTerms.getName(), Optional.empty());
273+
throw AggregationErrors.reduceTypeMismatch(referenceTerms.getName(), Optional.empty());
274274
}
275275
otherDocCount[0] += terms.getSumOfOtherDocCounts();
276276
final long thisAggDocCountError = getDocCountError(terms);

server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/GlobalOrdinalsStringTermsAggregator.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ public GlobalOrdinalsStringTermsAggregator(
9898
} else {
9999
this.collectionStrategy = cardinality.map(estimate -> {
100100
if (estimate > 1) {
101+
// This is a 500 class error, because we should never be able to reach it.
101102
throw new AggregationExecutionException("Dense ords don't know how to collect from many buckets");
102103
}
103104
return new DenseGlobalOrds();

server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalMappedRareTerms.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ public InternalAggregation reduce(List<InternalAggregation> aggregations, Aggreg
107107
&& terms.getClass().equals(UnmappedRareTerms.class) == false) {
108108
// control gets into this loop when the same field name against which the query is executed
109109
// is of different types in different indices.
110-
throw AggregationErrors.reduceTypeMissmatch(referenceTerms.getName(), Optional.empty());
110+
throw AggregationErrors.reduceTypeMismatch(referenceTerms.getName(), Optional.empty());
111111
}
112112
for (B bucket : terms.getBuckets()) {
113113
List<B> bucketList = buckets.computeIfAbsent(bucket.getKey(), k -> new ArrayList<>());

server/src/main/java/org/elasticsearch/search/aggregations/pipeline/BucketMetricsPipelineAggregator.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public final InternalAggregation doReduce(Aggregations aggregations, Aggregation
5656
Aggregation currentAgg = aggregation;
5757
while (currElement < parsedPath.size() - 1) {
5858
if (currentAgg == null) {
59-
throw new IllegalArgumentException(
59+
throw new AggregationExecutionException.InvalidPath(
6060
"bucket_path ["
6161
+ bucketsPaths()[0]
6262
+ "] expected aggregation with name ["
@@ -73,7 +73,8 @@ public final InternalAggregation doReduce(Aggregations aggregations, Aggregation
7373
parsedPath.get(currElement).key()
7474
);
7575
if (bucket == null) {
76-
throw new AggregationExecutionException(
76+
// seems not retryable, and therefore should be 400?
77+
throw new AggregationExecutionException.InvalidPath(
7778
"missing bucket ["
7879
+ parsedPath.get(currElement).key()
7980
+ "] for agg ["
@@ -84,13 +85,15 @@ public final InternalAggregation doReduce(Aggregations aggregations, Aggregation
8485
);
8586
}
8687
if (currElement == parsedPath.size() - 1) {
88+
// Seems not retryable, should be 400
8789
throw new AggregationExecutionException(
8890
"invalid bucket path ends at [" + parsedPath.get(currElement).key() + "]"
8991
);
9092
}
9193
currentAgg = bucket.getAggregations().get(parsedPath.get(++currElement).name());
9294
} else {
93-
throw new AggregationExecutionException(
95+
// Seems not retryable, should be 400
96+
throw new AggregationExecutionException.InvalidPath(
9497
"bucket_path ["
9598
+ bucketsPaths()[0]
9699
+ "] indicates bucket_key ["
@@ -107,10 +110,11 @@ public final InternalAggregation doReduce(Aggregations aggregations, Aggregation
107110
}
108111
}
109112
if (currentAgg instanceof InternalMultiBucketAggregation == false) {
113+
// Seems not retryable, should be 400
110114
String msg = currentAgg == null
111115
? "did not find multi-bucket aggregation for extraction."
112116
: "did not find multi-bucket aggregation for extraction. Found [" + currentAgg.getName() + "]";
113-
throw new AggregationExecutionException(msg);
117+
throw new AggregationExecutionException.InvalidPath(msg);
114118
}
115119
List<String> sublistedPath = AggregationPath.pathElementsAsStringList(parsedPath.subList(currElement, parsedPath.size()));
116120
// First element is the current agg, so we want the rest of the path

server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceRegistry.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ public <T> T getAggregator(RegistryKey<T> registryKey, ValuesSourceConfig values
183183
}
184184
return supplier;
185185
}
186+
// This should be a startup error. Should never happen, probably indicates a bad plugin if it does. Should probably log and have
187+
// actual docs on how to resolve.
186188
throw new AggregationExecutionException(
187189
"Unregistered Aggregation [" + (registryKey != null ? registryKey.getName() : "unknown aggregation") + "]"
188190
);

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
import org.elasticsearch.search.SearchException;
7777
import org.elasticsearch.search.SearchShardTarget;
7878
import org.elasticsearch.search.TooManyScrollContextsException;
79+
import org.elasticsearch.search.aggregations.AggregationExecutionException;
7980
import org.elasticsearch.search.aggregations.MultiBucketConsumerService;
8081
import org.elasticsearch.search.aggregations.UnsupportedAggregationOnDownsampledIndex;
8182
import org.elasticsearch.search.internal.ShardSearchContextId;
@@ -836,6 +837,7 @@ public void testIds() {
836837
ids.put(171, ApiNotAvailableException.class);
837838
ids.put(172, RecoveryCommitTooNewException.class);
838839
ids.put(173, TooManyScrollContextsException.class);
840+
ids.put(174, AggregationExecutionException.InvalidPath.class);
839841

840842
Map<Class<? extends ElasticsearchException>, Integer> reverse = new HashMap<>();
841843
for (Map.Entry<Integer, Class<? extends ElasticsearchException>> entry : ids.entrySet()) {

server/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1730,8 +1730,8 @@ public void testOrderByPipelineAggregation() throws Exception {
17301730

17311731
MappedFieldType fieldType = new KeywordFieldMapper.KeywordFieldType("field");
17321732

1733-
AggregationExecutionException e = expectThrows(
1734-
AggregationExecutionException.class,
1733+
AggregationExecutionException.InvalidPath e = expectThrows(
1734+
AggregationExecutionException.InvalidPath.class,
17351735
() -> searchAndReduce(indexReader, new AggTestConfig(termsAgg, fieldType))
17361736
);
17371737
assertEquals(

0 commit comments

Comments
 (0)