Skip to content

Commit 2b5f6ce

Browse files
authored
Add extra round trip to aggs tests (#79638)
This tries to automatically detect problems seriealization aggregation results by round tripping the results in our usual `AggregatorTestCase`. It's "free" testing in that we already have the tests written and we'll get round trip testing "on the side". But it's kind of sneaky because we aren't *trying* to test serialization here. So they failures can be surprising. But surprising test failures are better than bugs. At least that is what I tell myself so I can sleep at night. Closes #73680
1 parent c017e1a commit 2b5f6ce

File tree

16 files changed

+328
-98
lines changed

16 files changed

+328
-98
lines changed

server/src/main/java/org/elasticsearch/search/DocValueFormat.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,26 @@ public double parseDouble(String value, boolean roundUp, LongSupplier now) {
317317
public String toString() {
318318
return "DocValueFormat.DateTime(" + formatter + ", " + timeZone + ", " + resolution + ")";
319319
}
320+
321+
@Override
322+
public boolean equals(Object o) {
323+
if (this == o) {
324+
return true;
325+
}
326+
if (o == null || getClass() != o.getClass()) {
327+
return false;
328+
}
329+
DateTime that = (DateTime) o;
330+
return formatter.equals(that.formatter)
331+
&& timeZone.equals(that.timeZone)
332+
&& resolution == that.resolution
333+
&& formatSortValues == that.formatSortValues;
334+
}
335+
336+
@Override
337+
public int hashCode() {
338+
return Objects.hash(formatter, timeZone, resolution, formatSortValues);
339+
}
320340
}
321341

322342
DocValueFormat GEOHASH = GeoHashDocValueFormat.INSTANCE;

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.elasticsearch.common.io.stream.StreamOutput;
1212
import org.elasticsearch.search.DocValueFormat;
1313
import org.elasticsearch.search.aggregations.Aggregations;
14+
import org.elasticsearch.search.aggregations.Aggregator;
1415
import org.elasticsearch.search.aggregations.InternalAggregation;
1516
import org.elasticsearch.search.aggregations.InternalAggregations;
1617
import org.elasticsearch.search.aggregations.InternalMultiBucketAggregation;
@@ -52,7 +53,12 @@ public interface Reader<B extends Bucket<B>> {
5253
long subsetSize;
5354
long supersetDf;
5455
long supersetSize;
55-
long bucketOrd;
56+
/**
57+
* Ordinal of the bucket while it is being built. Not used after it is
58+
* returned from {@link Aggregator#buildAggregations(long[])} and not
59+
* serialized.
60+
*/
61+
transient long bucketOrd;
5662
double score;
5763
protected InternalAggregations aggregations;
5864
final transient DocValueFormat format;
@@ -134,15 +140,14 @@ public boolean equals(Object o) {
134140
}
135141

136142
Bucket<?> that = (Bucket<?>) o;
137-
return bucketOrd == that.bucketOrd
138-
&& Double.compare(that.score, score) == 0
143+
return Double.compare(that.score, score) == 0
139144
&& Objects.equals(aggregations, that.aggregations)
140145
&& Objects.equals(format, that.format);
141146
}
142147

143148
@Override
144149
public int hashCode() {
145-
return Objects.hash(getClass(), bucketOrd, aggregations, score, format);
150+
return Objects.hash(getClass(), aggregations, score, format);
146151
}
147152

148153
@Override

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

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,17 +142,23 @@ public boolean equals(Object obj) {
142142
return false;
143143
}
144144
Bucket<?> that = (Bucket<?>) obj;
145-
// No need to take format and showDocCountError, they are attributes
146-
// of the parent terms aggregation object that are only copied here
147-
// for serialization purposes
145+
if (showDocCountError && docCountError != that.docCountError) {
146+
/*
147+
* docCountError doesn't matter if not showing it and
148+
* serialization sets it to -1 no matter what it was
149+
* before.
150+
*/
151+
return false;
152+
}
148153
return Objects.equals(docCount, that.docCount)
149-
&& Objects.equals(docCountError, that.docCountError)
154+
&& Objects.equals(showDocCountError, that.showDocCountError)
155+
&& Objects.equals(format, that.format)
150156
&& Objects.equals(aggregations, that.aggregations);
151157
}
152158

153159
@Override
154160
public int hashCode() {
155-
return Objects.hash(getClass(), docCount, docCountError, aggregations);
161+
return Objects.hash(getClass(), docCount, format, showDocCountError, showDocCountError ? docCountError : -1, aggregations);
156162
}
157163
}
158164

server/src/main/java/org/elasticsearch/search/aggregations/metrics/InternalCardinality.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public XContentBuilder doXContentBody(XContentBuilder builder, Params params) th
101101

102102
@Override
103103
public int hashCode() {
104-
return Objects.hash(super.hashCode(), counts.hashCode(0));
104+
return Objects.hash(super.hashCode(), counts == null ? 0 : counts.hashCode(0));
105105
}
106106

107107
@Override
@@ -111,6 +111,9 @@ public boolean equals(Object obj) {
111111
if (super.equals(obj) == false) return false;
112112

113113
InternalCardinality other = (InternalCardinality) obj;
114+
if (counts == null) {
115+
return other.counts == null;
116+
}
114117
return counts.equals(0, other.counts, 0);
115118
}
116119

server/src/main/java/org/elasticsearch/search/aggregations/metrics/InternalTopHits.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,6 @@ public boolean equals(Object obj) {
206206
ScoreDoc otherDoc = other.topDocs.topDocs.scoreDocs[d];
207207
if (thisDoc.doc != otherDoc.doc) return false;
208208
if (Double.compare(thisDoc.score, otherDoc.score) != 0) return false;
209-
if (thisDoc.shardIndex != otherDoc.shardIndex) return false;
210209
if (thisDoc instanceof FieldDoc) {
211210
if (false == (otherDoc instanceof FieldDoc)) return false;
212211
FieldDoc thisFieldDoc = (FieldDoc) thisDoc;
@@ -231,7 +230,6 @@ public int hashCode() {
231230
ScoreDoc doc = topDocs.topDocs.scoreDocs[d];
232231
hashCode = 31 * hashCode + doc.doc;
233232
hashCode = 31 * hashCode + Float.floatToIntBits(doc.score);
234-
hashCode = 31 * hashCode + doc.shardIndex;
235233
if (doc instanceof FieldDoc) {
236234
FieldDoc fieldDoc = (FieldDoc) doc;
237235
hashCode = 31 * hashCode + Arrays.hashCode(fieldDoc.fields);

server/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalAutoDateHistogramTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ protected InternalAutoDateHistogram createTestInstance(String name, Map<String,
7474
randomLongBetween(0, utcMillis("2050-01-01")),
7575
roundingInfos,
7676
roundingIndex,
77-
randomNumericDocValueFormat()
77+
randomDateDocValueFormat()
7878
);
7979
}
8080

@@ -111,7 +111,7 @@ protected List<InternalAutoDateHistogram> randomResultsToReduce(String name, int
111111
long startingDate = randomLongBetween(0, utcMillis("2050-01-01"));
112112
RoundingInfo[] roundingInfos = AutoDateHistogramAggregationBuilder.buildRoundings(null, null);
113113
int roundingIndex = between(0, roundingInfos.length - 1);
114-
DocValueFormat format = randomNumericDocValueFormat();
114+
DocValueFormat format = randomDateDocValueFormat();
115115
List<InternalAutoDateHistogram> result = new ArrayList<>(size);
116116
for (int i = 0; i < size; i++) {
117117
long thisResultStart = startingDate;

server/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogramTests.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@
1212
import org.elasticsearch.common.Rounding.DateTimeUnit;
1313
import org.elasticsearch.core.TimeValue;
1414
import org.elasticsearch.index.mapper.DateFieldMapper;
15+
import org.elasticsearch.index.mapper.DateFieldMapper.Resolution;
1516
import org.elasticsearch.search.DocValueFormat;
1617
import org.elasticsearch.search.aggregations.BucketOrder;
1718
import org.elasticsearch.search.aggregations.InternalAggregations;
1819
import org.elasticsearch.test.InternalMultiBucketAggregationTestCase;
1920

21+
import java.time.ZoneOffset;
2022
import java.time.ZonedDateTime;
2123
import java.util.ArrayList;
2224
import java.util.HashMap;
@@ -41,7 +43,7 @@ public class InternalDateHistogramTests extends InternalMultiBucketAggregationTe
4143
public void setUp() throws Exception {
4244
super.setUp();
4345
keyed = randomBoolean();
44-
format = randomNumericDocValueFormat();
46+
format = randomDateDocValueFormat();
4547
// in order for reduction to work properly (and be realistic) we need to use the same interval, minDocCount, emptyBucketInfo
4648
// and base in all randomly created aggs as part of the same test run. This is particularly important when minDocCount is
4749
// set to 0 as empty buckets need to be added to fill the holes.
@@ -69,6 +71,26 @@ public void setUp() throws Exception {
6971

7072
@Override
7173
protected InternalDateHistogram createTestInstance(String name, Map<String, Object> metadata, InternalAggregations aggregations) {
74+
return createTestInstance(name, metadata, aggregations, format);
75+
}
76+
77+
@Override
78+
protected InternalDateHistogram createTestInstanceForXContent(String name, Map<String, Object> metadata, InternalAggregations subAggs) {
79+
// We have to force a format that won't throw away precision and cause duplicate fields
80+
DocValueFormat xContentCompatibleFormat = new DocValueFormat.DateTime(
81+
DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER,
82+
ZoneOffset.UTC,
83+
Resolution.MILLISECONDS
84+
);
85+
return createTestInstance(name, metadata, subAggs, xContentCompatibleFormat);
86+
}
87+
88+
private InternalDateHistogram createTestInstance(
89+
String name,
90+
Map<String, Object> metadata,
91+
InternalAggregations aggregations,
92+
DocValueFormat format
93+
) {
7294
int nbBuckets = randomNumberOfBuckets();
7395
List<InternalDateHistogram.Bucket> buckets = new ArrayList<>(nbBuckets);
7496
// avoid having different random instance start from exactly the same base

server/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalDateRangeTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public class InternalDateRangeTests extends InternalRangeTestCase<InternalDateRa
3131
@Override
3232
public void setUp() throws Exception {
3333
super.setUp();
34-
format = randomNumericDocValueFormat();
34+
format = randomDateDocValueFormat();
3535

3636
Function<ZonedDateTime, ZonedDateTime> interval = randomFrom(
3737
dateTime -> dateTime.plusSeconds(1),

server/src/test/java/org/elasticsearch/search/aggregations/bucket/range/RangeAggregatorTests.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -433,11 +433,11 @@ public void testUnmappedWithBadMissingField() {
433433
public void testSubAggCollectsFromSingleBucketIfOneRange() throws IOException {
434434
RangeAggregationBuilder aggregationBuilder = new RangeAggregationBuilder("test").field(NUMBER_FIELD_NAME)
435435
.addRange(0d, 10d)
436-
.subAggregation(aggCardinality("c"));
436+
.subAggregation(aggCardinalityUpperBound("c"));
437437

438438
simpleTestCase(aggregationBuilder, new MatchAllDocsQuery(), range -> {
439439
List<? extends InternalRange.Bucket> ranges = range.getBuckets();
440-
InternalAggCardinality pc = ranges.get(0).getAggregations().get("c");
440+
InternalAggCardinalityUpperBound pc = ranges.get(0).getAggregations().get("c");
441441
assertThat(pc.cardinality(), equalTo(CardinalityUpperBound.ONE));
442442
});
443443
}
@@ -446,11 +446,11 @@ public void testSubAggCollectsFromManyBucketsIfManyRanges() throws IOException {
446446
RangeAggregationBuilder aggregationBuilder = new RangeAggregationBuilder("test").field(NUMBER_FIELD_NAME)
447447
.addRange(0d, 10d)
448448
.addRange(10d, 100d)
449-
.subAggregation(aggCardinality("c"));
449+
.subAggregation(aggCardinalityUpperBound("c"));
450450

451451
simpleTestCase(aggregationBuilder, new MatchAllDocsQuery(), range -> {
452452
List<? extends InternalRange.Bucket> ranges = range.getBuckets();
453-
InternalAggCardinality pc = ranges.get(0).getAggregations().get("c");
453+
InternalAggCardinalityUpperBound pc = ranges.get(0).getAggregations().get("c");
454454
assertThat(pc.cardinality().map(i -> i), equalTo(2));
455455
pc = ranges.get(1).getAggregations().get("c");
456456
assertThat(pc.cardinality().map(i -> i), equalTo(2));

0 commit comments

Comments
 (0)