Skip to content

Commit b459f27

Browse files
ivanceancordon
authored andcommitted
Aggs: Unmute and fix CCSDuelIT flaky tests (elastic#138375)
Fixes elastic#132879 Fixed elastic#132880 Ignore `doc_count_error_upper_bound` in response comparison for terms aggs in CCSDuelIT, and unmute tests. It was failing sometimes with a: `doc_count_error_upper_bound: expected Integer [5] but was Integer [2]` Given it's an estimation (From https://www.elastic.co/docs/reference/aggregations/search-aggregations-bucket-terms-aggregation), ignoring should be safe for the test purposes.
1 parent 42b2203 commit b459f27

File tree

4 files changed

+147
-26
lines changed

4 files changed

+147
-26
lines changed

muted-tests.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -252,12 +252,6 @@ tests:
252252
- class: org.elasticsearch.xpack.eql.planner.QueryTranslatorTests
253253
method: testMatchOptimization
254254
issue: https://github.com/elastic/elasticsearch/issues/132894
255-
- class: org.elasticsearch.search.CCSDuelIT
256-
method: testTermsAggs
257-
issue: https://github.com/elastic/elasticsearch/issues/132879
258-
- class: org.elasticsearch.search.CCSDuelIT
259-
method: testTermsAggsWithProfile
260-
issue: https://github.com/elastic/elasticsearch/issues/132880
261255
- class: org.elasticsearch.xpack.security.authc.AuthenticationServiceTests
262256
method: testInvalidToken
263257
issue: https://github.com/elastic/elasticsearch/issues/133328

qa/multi-cluster-search/src/test/java/org/elasticsearch/search/CCSDuelIT.java

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
import java.util.concurrent.CountDownLatch;
9797
import java.util.concurrent.TimeUnit;
9898
import java.util.concurrent.atomic.AtomicReference;
99+
import java.util.function.Predicate;
99100

100101
import static org.hamcrest.CoreMatchers.anyOf;
101102
import static org.hamcrest.CoreMatchers.equalTo;
@@ -675,12 +676,12 @@ public void testTermsAggs() throws Exception {
675676
{
676677
SearchRequest searchRequest = initLocalAndRemoteSearchRequest();
677678
searchRequest.source(buildTermsAggsSource());
678-
duelRequest(searchRequest, CCSDuelIT::assertAggs);
679+
duelRequest(searchRequest, CCSDuelIT::assertAggs, true, path -> path.endsWith("/doc_count_error_upper_bound") == false);
679680
}
680681
{
681682
SearchRequest searchRequest = initRemoteOnlySearchRequest();
682683
searchRequest.source(buildTermsAggsSource());
683-
duelRequest(searchRequest, CCSDuelIT::assertAggs);
684+
duelRequest(searchRequest, CCSDuelIT::assertAggs, true, path -> path.endsWith("/doc_count_error_upper_bound") == false);
684685
}
685686
}
686687

@@ -689,12 +690,12 @@ public void testTermsAggsWithProfile() throws Exception {
689690
{
690691
SearchRequest searchRequest = initLocalAndRemoteSearchRequest();
691692
searchRequest.source(buildTermsAggsSource().profile(true));
692-
duelRequest(searchRequest, CCSDuelIT::assertAggs);
693+
duelRequest(searchRequest, CCSDuelIT::assertAggs, true, path -> path.endsWith("/doc_count_error_upper_bound") == false);
693694
}
694695
{
695696
SearchRequest searchRequest = initRemoteOnlySearchRequest();
696697
searchRequest.source(buildTermsAggsSource().profile(true));
697-
duelRequest(searchRequest, CCSDuelIT::assertAggs);
698+
duelRequest(searchRequest, CCSDuelIT::assertAggs, true, path -> path.endsWith("/doc_count_error_upper_bound") == false);
698699
}
699700
}
700701

@@ -875,7 +876,7 @@ public void testShardFailures() throws Exception {
875876
assertNull(response.evaluate("suggest"));
876877
assertThat(response.evaluateArraySize("hits.hits"), greaterThan(0));
877878
assertThat(response.evaluate("_shards.failed"), greaterThanOrEqualTo(2));
878-
}, compareAsyncAndSyncResponses);
879+
}, compareAsyncAndSyncResponses, path -> true);
879880
}
880881

881882
public void testTermSuggester() throws Exception {
@@ -984,18 +985,19 @@ private static SearchRequest initRemoteOnlySearchRequest() {
984985
}
985986

986987
private void duelRequest(SearchRequest searchRequest, CheckedConsumer<ObjectPath, IOException> responseChecker) throws Exception {
987-
duelRequest(searchRequest, responseChecker, true);
988+
duelRequest(searchRequest, responseChecker, true, p -> true);
988989
}
989990

990991
private void duelRequest(
991992
SearchRequest searchRequest,
992993
CheckedConsumer<ObjectPath, IOException> responseChecker,
993-
boolean compareAsyncToSyncResponses
994+
boolean compareAsyncToSyncResponses,
995+
Predicate<String> pathFilter
994996
) throws Exception {
995-
Map<String, Object> syncResponseMap = duelSearchSync(searchRequest, responseChecker);
996-
Map<String, Object> asyncResponseMap = duelSearchAsync(searchRequest, responseChecker);
997+
Map<String, Object> syncResponseMap = duelSearchSync(searchRequest, responseChecker, pathFilter);
998+
Map<String, Object> asyncResponseMap = duelSearchAsync(searchRequest, responseChecker, pathFilter);
997999
if (compareAsyncToSyncResponses) {
998-
compareResponseMaps(syncResponseMap, asyncResponseMap, "Comparing sync_search CCS vs. async_search CCS");
1000+
compareResponseMaps(syncResponseMap, asyncResponseMap, "Comparing sync_search CCS vs. async_search CCS", pathFilter);
9991001
}
10001002
}
10011003

@@ -1004,11 +1006,24 @@ private void duelRequest(
10041006
*/
10051007
private static Map<String, Object> duelSearchSync(SearchRequest searchRequest, CheckedConsumer<ObjectPath, IOException> responseChecker)
10061008
throws Exception {
1009+
return duelSearchSync(searchRequest, responseChecker, p -> true);
1010+
}
1011+
1012+
/**
1013+
* @return responseMap from one of the Synchronous Search Requests
1014+
*/
1015+
private static Map<String, Object> duelSearchSync(
1016+
SearchRequest searchRequest,
1017+
CheckedConsumer<ObjectPath, IOException> responseChecker,
1018+
Predicate<String> pathFilter
1019+
) throws Exception {
10071020
CountDownLatch latch = new CountDownLatch(2);
1021+
10081022
AtomicReference<Exception> exception1 = new AtomicReference<>();
10091023
AtomicReference<Response> minimizeRoundtripsResponse = new AtomicReference<>();
10101024
searchRequest.setCcsMinimizeRoundtrips(true);
10111025
submitSyncSearch(searchRequest, minimizeRoundtripsResponse, exception1, latch);
1026+
10121027
AtomicReference<Exception> exception2 = new AtomicReference<>();
10131028
AtomicReference<Response> fanOutResponse = new AtomicReference<>();
10141029
searchRequest.setCcsMinimizeRoundtrips(false);
@@ -1075,7 +1090,12 @@ private static Map<String, Object> duelSearchSync(SearchRequest searchRequest, C
10751090
Map<String, Object> minimizeRoundtripsResponseMap = responseToMap(minimizeRoundtripsSearchResponse);
10761091
if (minimizeRoundtripsSearchResponse.evaluate("_clusters") != null && fanOutSearchResponse.evaluate("_clusters") != null) {
10771092
Map<String, Object> fanOutResponseMap = responseToMap(fanOutSearchResponse);
1078-
compareResponseMaps(minimizeRoundtripsResponseMap, fanOutResponseMap, "Comparing sync_search minimizeRoundTrip vs. fanOut");
1093+
compareResponseMaps(
1094+
minimizeRoundtripsResponseMap,
1095+
fanOutResponseMap,
1096+
"Comparing sync_search minimizeRoundTrip vs. fanOut",
1097+
pathFilter
1098+
);
10791099
assertThat(
10801100
minimizeRoundtripsSearchResponse.evaluate("_shards.skipped"),
10811101
lessThanOrEqualTo((Integer) fanOutSearchResponse.evaluate("_shards.skipped"))
@@ -1123,6 +1143,17 @@ public void onFailure(Exception exception) {
11231143
private static Map<String, Object> duelSearchAsync(
11241144
SearchRequest searchRequest,
11251145
CheckedConsumer<ObjectPath, IOException> responseChecker
1146+
) throws Exception {
1147+
return duelSearchAsync(searchRequest, responseChecker, p -> true);
1148+
}
1149+
1150+
/**
1151+
* @return responseMap from one of the async searches
1152+
*/
1153+
private static Map<String, Object> duelSearchAsync(
1154+
SearchRequest searchRequest,
1155+
CheckedConsumer<ObjectPath, IOException> responseChecker,
1156+
Predicate<String> pathFilter
11261157
) throws Exception {
11271158
searchRequest.setCcsMinimizeRoundtrips(true);
11281159
ObjectPath minimizeRoundtripsResponse = submitAsyncSearch(searchRequest, TimeValue.timeValueSeconds(1));
@@ -1183,7 +1214,12 @@ private static Map<String, Object> duelSearchAsync(
11831214
Map<String, Object> minimizeRoundtripsResponseMap = responseToMap(minimizeRoundtripsResponse);
11841215
if (minimizeRoundtripsResponse.evaluate("_clusters") != null && fanOutResponse.evaluate("_clusters") != null) {
11851216
Map<String, Object> fanOutResponseMap = responseToMap(fanOutResponse);
1186-
compareResponseMaps(minimizeRoundtripsResponseMap, fanOutResponseMap, "Comparing async_search minimizeRoundTrip vs. fanOut");
1217+
compareResponseMaps(
1218+
minimizeRoundtripsResponseMap,
1219+
fanOutResponseMap,
1220+
"Comparing async_search minimizeRoundTrip vs. fanOut",
1221+
pathFilter
1222+
);
11871223
assertThat(
11881224
minimizeRoundtripsResponse.evaluate("_shards.skipped"),
11891225
lessThanOrEqualTo((Integer) fanOutResponse.evaluate("_shards.skipped"))
@@ -1192,8 +1228,13 @@ private static Map<String, Object> duelSearchAsync(
11921228
return minimizeRoundtripsResponseMap;
11931229
}
11941230

1195-
private static void compareResponseMaps(Map<String, Object> responseMap1, Map<String, Object> responseMap2, String info) {
1196-
String diff = XContentTestUtils.differenceBetweenMapsIgnoringArrayOrder(responseMap1, responseMap2);
1231+
private static void compareResponseMaps(
1232+
Map<String, Object> responseMap1,
1233+
Map<String, Object> responseMap2,
1234+
String info,
1235+
Predicate<String> pathFilter
1236+
) {
1237+
String diff = XContentTestUtils.differenceBetweenMapsIgnoringArrayOrder(responseMap1, responseMap2, pathFilter);
11971238
if (diff != null) {
11981239
NotEqualMessageBuilder builder = new NotEqualMessageBuilder();
11991240
builder.compareMaps(responseMap1, responseMap2);

test/framework/src/main/java/org/elasticsearch/test/XContentTestUtils.java

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,16 +82,39 @@ public static BytesReference convertToXContent(Map<String, ?> map, XContentType
8282
* @return null if maps are equal or path to the element where the difference was found
8383
*/
8484
public static String differenceBetweenMapsIgnoringArrayOrder(Map<String, Object> first, Map<String, Object> second) {
85-
return differenceBetweenMapsIgnoringArrayOrder("", first, second);
85+
return differenceBetweenMapsIgnoringArrayOrder("", first, second, p -> true);
8686
}
8787

88-
private static String differenceBetweenMapsIgnoringArrayOrder(String path, Map<String, Object> first, Map<String, Object> second) {
88+
/**
89+
* Compares two maps generated from XContentObjects. The order of elements in arrays is ignored.
90+
*
91+
* @param pathFilter Predicate to filter a path and its children. True if the path should be checked, false to exclude it.
92+
* @return null if maps are equal or path to the element where the difference was found
93+
*/
94+
public static String differenceBetweenMapsIgnoringArrayOrder(
95+
Map<String, Object> first,
96+
Map<String, Object> second,
97+
Predicate<String> pathFilter
98+
) {
99+
return differenceBetweenMapsIgnoringArrayOrder("", first, second, pathFilter);
100+
}
101+
102+
private static String differenceBetweenMapsIgnoringArrayOrder(
103+
String path,
104+
Map<String, Object> first,
105+
Map<String, Object> second,
106+
Predicate<String> pathFilter
107+
) {
108+
if (pathFilter.test(path) == false) {
109+
return null;
110+
}
111+
89112
if (first.size() != second.size()) {
90113
return path + ": sizes of the maps don't match: " + first.size() + " != " + second.size();
91114
}
92115

93116
for (String key : first.keySet()) {
94-
String reason = differenceBetweenObjectsIgnoringArrayOrder(path + "/" + key, first.get(key), second.get(key));
117+
String reason = differenceBetweenObjectsIgnoringArrayOrder(path + "/" + key, first.get(key), second.get(key), pathFilter);
95118
if (reason != null) {
96119
return reason;
97120
}
@@ -100,7 +123,16 @@ private static String differenceBetweenMapsIgnoringArrayOrder(String path, Map<S
100123
}
101124

102125
@SuppressWarnings("unchecked")
103-
private static String differenceBetweenObjectsIgnoringArrayOrder(String path, Object first, Object second) {
126+
private static String differenceBetweenObjectsIgnoringArrayOrder(
127+
String path,
128+
Object first,
129+
Object second,
130+
Predicate<String> pathFilter
131+
) {
132+
if (pathFilter.test(path) == false) {
133+
return null;
134+
}
135+
104136
if (first == null) {
105137
if (second == null) {
106138
return null;
@@ -116,7 +148,7 @@ private static String differenceBetweenObjectsIgnoringArrayOrder(String path, Ob
116148
for (Object firstObj : firstList) {
117149
boolean found = false;
118150
for (Object secondObj : secondList) {
119-
reason = differenceBetweenObjectsIgnoringArrayOrder(path + "/*", firstObj, secondObj);
151+
reason = differenceBetweenObjectsIgnoringArrayOrder(path + "/*", firstObj, secondObj, pathFilter);
120152
if (reason == null) {
121153
secondList.remove(secondObj);
122154
found = true;
@@ -140,7 +172,7 @@ private static String differenceBetweenObjectsIgnoringArrayOrder(String path, Ob
140172
}
141173
} else if (first instanceof Map) {
142174
if (second instanceof Map) {
143-
return differenceBetweenMapsIgnoringArrayOrder(path, (Map<String, Object>) first, (Map<String, Object>) second);
175+
return differenceBetweenMapsIgnoringArrayOrder(path, (Map<String, Object>) first, (Map<String, Object>) second, pathFilter);
144176
} else {
145177
return path + ": the second element is not a map (got " + second + ")";
146178
}

test/framework/src/test/java/org/elasticsearch/test/XContentTestUtilsTests.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@
2727
import java.util.Map;
2828
import java.util.function.Predicate;
2929

30+
import static org.elasticsearch.test.XContentTestUtils.differenceBetweenMapsIgnoringArrayOrder;
3031
import static org.elasticsearch.test.XContentTestUtils.insertRandomFields;
3132
import static org.hamcrest.Matchers.equalTo;
3233
import static org.hamcrest.Matchers.hasItem;
3334
import static org.hamcrest.Matchers.instanceOf;
35+
import static org.hamcrest.Matchers.nullValue;
3436

3537
public class XContentTestUtilsTests extends ESTestCase {
3638

@@ -222,4 +224,56 @@ public void testInsertRandomXContent() throws IOException {
222224
assertEquals(1, foo4List.size());
223225
assertEquals(2, ((Map<String, Object>) foo4List.get(0)).keySet().size());
224226
}
227+
228+
public void testDifferenceBetweenMapsIgnoringArrayOrder() {
229+
var map1 = Map.of("foo", List.of(1, 2, 3), "bar", Map.of("a", 2, "b", List.of(3, 2, 1)), "baz", List.of(3, 2, 1, "test"));
230+
var map2 = Map.of("foo", List.of(3, 2, 1), "bar", Map.of("b", List.of(3, 1, 2), "a", 2), "baz", List.of(1, "test", 2, 3));
231+
232+
assertThat(differenceBetweenMapsIgnoringArrayOrder(map1, map2), nullValue());
233+
}
234+
235+
public void testDifferenceBetweenMapsIgnoringArrayOrder_WithFilter_Object() {
236+
var map1 = Map.of("foo", List.of(1, 2, 3), "bar", Map.of("a", 2, "b", List.of(3, 2, 1)), "different", Map.of("a", 1, "x", 8));
237+
var map2 = Map.of(
238+
"foo",
239+
List.of(3, 2, 1),
240+
"bar",
241+
Map.of("b", List.of(3, 1, 2), "a", 2),
242+
"different",
243+
Map.of("a", 1, "x", "different value")
244+
);
245+
246+
assertThat(
247+
differenceBetweenMapsIgnoringArrayOrder(map1, map2),
248+
equalTo("/different/x: the elements don't match: [8] != [different value]")
249+
);
250+
assertThat(differenceBetweenMapsIgnoringArrayOrder(map1, map2, path -> path.equals("/different/x") == false), nullValue());
251+
assertThat(differenceBetweenMapsIgnoringArrayOrder(map1, map2, path -> path.equals("/different") == false), nullValue());
252+
assertThat(differenceBetweenMapsIgnoringArrayOrder(map1, map2, path -> path.isEmpty() == false), nullValue());
253+
}
254+
255+
public void testDifferenceBetweenMapsIgnoringArrayOrder_WithFilter_Array() {
256+
var map1 = Map.of(
257+
"foo",
258+
List.of(1, 2, 3),
259+
"bar",
260+
Map.of("a", 2, "b", List.of(3, 2, 1)),
261+
"different",
262+
List.of(3, Map.of("x", 10), 1)
263+
);
264+
var map2 = Map.of(
265+
"foo",
266+
List.of(3, 2, 1),
267+
"bar",
268+
Map.of("b", List.of(3, 1, 2), "a", 2),
269+
"different",
270+
List.of(3, Map.of("x", 5), 1)
271+
);
272+
273+
assertThat(differenceBetweenMapsIgnoringArrayOrder(map1, map2), equalTo("/different/*: the second element is not a map (got 1)"));
274+
assertThat(differenceBetweenMapsIgnoringArrayOrder(map1, map2, path -> path.equals("/different/*/x") == false), nullValue());
275+
assertThat(differenceBetweenMapsIgnoringArrayOrder(map1, map2, path -> path.equals("/different/*") == false), nullValue());
276+
assertThat(differenceBetweenMapsIgnoringArrayOrder(map1, map2, path -> path.equals("/different") == false), nullValue());
277+
assertThat(differenceBetweenMapsIgnoringArrayOrder(map1, map2, path -> path.isEmpty() == false), nullValue());
278+
}
225279
}

0 commit comments

Comments
 (0)