77
88package org .elasticsearch .xpack .esql .action ;
99
10+ import org .elasticsearch .ElasticsearchException ;
1011import org .elasticsearch .client .internal .Client ;
1112import org .elasticsearch .common .Strings ;
1213import org .elasticsearch .common .settings .Settings ;
1314import org .elasticsearch .index .query .QueryBuilder ;
1415import org .elasticsearch .index .query .RangeQueryBuilder ;
16+ import org .elasticsearch .plugins .Plugin ;
1517import org .elasticsearch .test .junit .annotations .TestLogging ;
1618import org .elasticsearch .xpack .esql .VerificationException ;
1719
20+ import java .io .IOException ;
21+ import java .util .Collection ;
1822import java .util .HashSet ;
1923import java .util .List ;
2024import java .util .Map ;
@@ -37,6 +41,11 @@ protected Map<String, Boolean> skipUnavailableForRemoteClusters() {
3741 return Map .of (REMOTE_CLUSTER_1 , false , REMOTE_CLUSTER_2 , false );
3842 }
3943
44+ @ Override
45+ protected boolean reuseClusters () {
46+ return false ;
47+ }
48+
4049 protected void assertClusterMetadataSuccess (EsqlExecutionInfo .Cluster clusterMetatata , int shards , long took , String indexExpression ) {
4150 assertThat (clusterMetatata .getIndexExpression (), equalTo (indexExpression ));
4251 assertThat (clusterMetatata .getStatus (), equalTo (EsqlExecutionInfo .Cluster .Status .SUCCESSFUL ));
@@ -75,6 +84,17 @@ protected void assertClusterMetadataSkippedShards(
7584 assertThat (clusterMetatata .getFailedShards (), equalTo (0 ));
7685 }
7786
87+ protected void assertClusterMetadataSkipped (EsqlExecutionInfo .Cluster clusterMetatata , long took , String indexExpression ) {
88+ assertThat (clusterMetatata .getIndexExpression (), equalTo (indexExpression ));
89+ assertThat (clusterMetatata .getStatus (), equalTo (EsqlExecutionInfo .Cluster .Status .SKIPPED ));
90+ assertThat (clusterMetatata .getTook ().millis (), greaterThanOrEqualTo (0L ));
91+ assertThat (clusterMetatata .getTook ().millis (), lessThanOrEqualTo (took ));
92+ assertThat (clusterMetatata .getTotalShards (), equalTo (0 ));
93+ assertThat (clusterMetatata .getSuccessfulShards (), equalTo (0 ));
94+ assertThat (clusterMetatata .getSkippedShards (), equalTo (0 ));
95+ assertThat (clusterMetatata .getFailedShards (), equalTo (0 ));
96+ }
97+
7898 protected EsqlQueryResponse runQuery (String query , Boolean ccsMetadataInResponse , QueryBuilder filter ) {
7999 EsqlQueryRequest request = EsqlQueryRequest .syncEsqlQueryRequest ();
80100 request .query (query );
@@ -244,11 +264,12 @@ public void testFilterWithMissingIndex() {
244264 populateDateIndex (LOCAL_CLUSTER , LOCAL_INDEX , localShards , docsTest1 , "2024-11-26" );
245265 populateDateIndex (REMOTE_CLUSTER_1 , REMOTE_INDEX , remoteShards , docsTest2 , "2023-11-26" );
246266
247- int docSize = docsTest1 ;
267+ int count = 0 ;
248268 for (var filter : List .of (
249269 new RangeQueryBuilder ("@timestamp" ).from ("2024-01-01" ).to ("now" ),
250270 new RangeQueryBuilder ("@timestamp" ).from ("2025-01-01" ).to ("now" )
251271 )) {
272+ count ++;
252273 // Local index missing
253274 VerificationException e = expectThrows (
254275 VerificationException .class ,
@@ -264,31 +285,162 @@ public void testFilterWithMissingIndex() {
264285 // e = expectThrows(VerificationException.class, () -> runQuery("from missing,logs-1", randomBoolean(), filter).close());
265286 // assertThat(e.getDetailedMessage(), containsString("Unknown index [missing]"));
266287 // Local index missing + existing remote
267- e = expectThrows (VerificationException .class , () -> runQuery ("from missing,c* :logs-2" , randomBoolean (), filter ).close ());
288+ e = expectThrows (VerificationException .class , () -> runQuery ("from missing,cluster-a :logs-2" , randomBoolean (), filter ).close ());
268289 assertThat (e .getDetailedMessage (), containsString ("Unknown index [missing]" ));
269290 // Wildcard index missing
270291 e = expectThrows (VerificationException .class , () -> runQuery ("from missing*" , randomBoolean (), filter ).close ());
271292 assertThat (e .getDetailedMessage (), containsString ("Unknown index [missing*]" ));
272293 // Wildcard index missing + existing index
273294 try (EsqlQueryResponse resp = runQuery ("from missing*,logs-1" , randomBoolean (), filter )) {
274295 List <List <Object >> values = getValuesList (resp );
275- assertThat (values , hasSize (docSize ));
276- // for the second round
277- docSize = 0 ;
296+ assertThat (values , hasSize (count > 1 ? 0 : docsTest1 ));
278297 }
279298 }
280299 }
281300
282301 public void testFilterWithMissingRemoteIndex () {
283- // TODO
302+ int docsTest1 = 50 ;
303+ int docsTest2 = 30 ;
304+ int localShards = randomIntBetween (1 , 5 );
305+ int remoteShards = randomIntBetween (1 , 5 );
306+ populateDateIndex (LOCAL_CLUSTER , LOCAL_INDEX , localShards , docsTest1 , "2024-11-26" );
307+ populateDateIndex (REMOTE_CLUSTER_1 , REMOTE_INDEX , remoteShards , docsTest2 , "2023-11-26" );
308+
309+ int count = 0 ;
310+ for (var filter : List .of (
311+ new RangeQueryBuilder ("@timestamp" ).from ("2023-01-01" ).to ("now" ),
312+ new RangeQueryBuilder ("@timestamp" ).from ("2024-01-01" ).to ("now" ),
313+ new RangeQueryBuilder ("@timestamp" ).from ("2025-01-01" ).to ("now" )
314+ )) {
315+ count ++;
316+ // Local index missing
317+ VerificationException e = expectThrows (
318+ VerificationException .class ,
319+ () -> runQuery ("from cluster-a:missing" , randomBoolean (), filter ).close ()
320+ );
321+ assertThat (e .getDetailedMessage (), containsString ("Unknown index [cluster-a:missing]" ));
322+ // Local index missing + wildcards
323+ // FIXME: planner does not catch this now
324+ // e = expectThrows(VerificationException.class, () -> runQuery("from cluster-a:missing,cluster-a:logs*", randomBoolean(),
325+ // filter).close());
326+ // assertThat(e.getDetailedMessage(), containsString("Unknown index [cluster-a:missing]"));
327+ // Local index missing + existing index
328+ // FIXME: planner does not catch this now
329+ // e = expectThrows(VerificationException.class, () -> runQuery("from cluster-a:missing,cluster-a:logs-2", randomBoolean(),
330+ // filter).close());
331+ // assertThat(e.getDetailedMessage(), containsString("Unknown index [cluster-a:missing]"));
332+ // Local index + missing remote
333+ e = expectThrows (VerificationException .class , () -> runQuery ("from logs-1,cluster-a:missing" , randomBoolean (), filter ).close ());
334+ assertThat (e .getDetailedMessage (), containsString ("Unknown index [cluster-a:missing]" ));
335+ // Wildcard index missing
336+ e = expectThrows (VerificationException .class , () -> runQuery ("from cluster-a:missing*" , randomBoolean (), filter ).close ());
337+ assertThat (e .getDetailedMessage (), containsString ("Unknown index [cluster-a:missing*]" ));
338+ // Wildcard index missing + existing remote index
339+ try (EsqlQueryResponse resp = runQuery ("from cluster-a:missing*,cluster-a:logs-2" , randomBoolean (), filter )) {
340+ List <List <Object >> values = getValuesList (resp );
341+ assertThat (values , hasSize (count > 1 ? 0 : docsTest2 ));
342+ }
343+ // Wildcard index missing + existing local index
344+ try (EsqlQueryResponse resp = runQuery ("from cluster-a:missing*,logs-1" , randomBoolean (), filter )) {
345+ List <List <Object >> values = getValuesList (resp );
346+ assertThat (values , hasSize (count > 2 ? 0 : docsTest1 ));
347+ }
348+ }
284349 }
285350
286- public void testFilterWithUnavailableRemote () {
287- // TODO
351+ public void testFilterWithUnavailableRemote () throws IOException {
352+ int docsTest1 = 50 ;
353+ int localShards = randomIntBetween (1 , 5 );
354+ populateDateIndex (LOCAL_CLUSTER , LOCAL_INDEX , localShards , docsTest1 , "2024-11-26" );
355+ cluster (REMOTE_CLUSTER_1 ).close ();
356+
357+ for (var filter : List .of (
358+ new RangeQueryBuilder ("@timestamp" ).from ("2024-01-01" ).to ("now" ),
359+ new RangeQueryBuilder ("@timestamp" ).from ("2025-01-01" ).to ("now" )
360+ )) {
361+ // One index
362+ var e = expectThrows (ElasticsearchException .class , () -> runQuery ("from cluster-a:log-2" , randomBoolean (), filter ).close ());
363+ // Two indices
364+ e = expectThrows (ElasticsearchException .class , () -> runQuery ("from logs-1,cluster-a:log-2" , randomBoolean (), filter ).close ());
365+ // Wildcard
366+ e = expectThrows (ElasticsearchException .class , () -> runQuery ("from logs-1,cluster-a:log*" , randomBoolean (), filter ).close ());
367+ }
288368 }
289369
290- public void testFilterWithUnavailableRemoteAndSkipUnavailable () {
291- // TODO
370+ public void testFilterWithUnavailableRemoteAndSkipUnavailable () throws IOException {
371+ setSkipUnavailable (REMOTE_CLUSTER_1 , true );
372+ int docsTest1 = 50 ;
373+ int localShards = randomIntBetween (1 , 5 );
374+ populateDateIndex (LOCAL_CLUSTER , LOCAL_INDEX , localShards , docsTest1 , "2024-11-26" );
375+ cluster (REMOTE_CLUSTER_1 ).close ();
376+ int count = 0 ;
377+
378+ for (var filter : List .of (
379+ new RangeQueryBuilder ("@timestamp" ).from ("2024-01-01" ).to ("now" ),
380+ new RangeQueryBuilder ("@timestamp" ).from ("2025-01-01" ).to ("now" )
381+ )) {
382+ count ++;
383+ // One index
384+ try (EsqlQueryResponse resp = runQuery ("from cluster-a:logs-2" , randomBoolean (), filter )) {
385+ List <List <Object >> values = getValuesList (resp );
386+ assertThat (values , hasSize (0 ));
387+ EsqlExecutionInfo executionInfo = resp .getExecutionInfo ();
388+ assertNotNull (executionInfo );
389+ assertThat (executionInfo .isCrossClusterSearch (), is (true ));
390+ long overallTookMillis = executionInfo .overallTook ().millis ();
391+ assertThat (overallTookMillis , greaterThanOrEqualTo (0L ));
392+
393+ assertThat (executionInfo .clusterAliases (), equalTo (Set .of (REMOTE_CLUSTER_1 )));
394+
395+ EsqlExecutionInfo .Cluster remoteCluster = executionInfo .getCluster (REMOTE_CLUSTER_1 );
396+ assertClusterMetadataSkipped (remoteCluster , overallTookMillis , "logs-2" );
397+ }
398+ // Two indices
399+ try (EsqlQueryResponse resp = runQuery ("from logs-1,cluster-a:logs-2" , randomBoolean (), filter )) {
400+ List <List <Object >> values = getValuesList (resp );
401+ assertThat (values , hasSize (count > 1 ? 0 : docsTest1 ));
402+ EsqlExecutionInfo executionInfo = resp .getExecutionInfo ();
403+ assertNotNull (executionInfo );
404+ assertThat (executionInfo .isCrossClusterSearch (), is (true ));
405+ long overallTookMillis = executionInfo .overallTook ().millis ();
406+ assertThat (overallTookMillis , greaterThanOrEqualTo (0L ));
407+
408+ assertThat (executionInfo .clusterAliases (), equalTo (Set .of (LOCAL_CLUSTER , REMOTE_CLUSTER_1 )));
409+
410+ EsqlExecutionInfo .Cluster remoteCluster = executionInfo .getCluster (REMOTE_CLUSTER_1 );
411+ assertClusterMetadataSkipped (remoteCluster , overallTookMillis , "logs-2" );
412+
413+ EsqlExecutionInfo .Cluster localCluster = executionInfo .getCluster (LOCAL_CLUSTER );
414+ if (count > 1 ) {
415+ assertClusterMetadataNoShards (localCluster , localShards , overallTookMillis , "logs-1" );
416+ } else {
417+ assertClusterMetadataSuccess (localCluster , localShards , overallTookMillis , "logs-1" );
418+ }
419+ }
420+ // Wildcard
421+ try (EsqlQueryResponse resp = runQuery ("from logs-1,cluster-a:logs*" , randomBoolean (), filter )) {
422+ List <List <Object >> values = getValuesList (resp );
423+ assertThat (values , hasSize (count > 1 ? 0 : docsTest1 ));
424+ EsqlExecutionInfo executionInfo = resp .getExecutionInfo ();
425+ assertNotNull (executionInfo );
426+ assertThat (executionInfo .isCrossClusterSearch (), is (true ));
427+ long overallTookMillis = executionInfo .overallTook ().millis ();
428+ assertThat (overallTookMillis , greaterThanOrEqualTo (0L ));
429+
430+ assertThat (executionInfo .clusterAliases (), equalTo (Set .of (LOCAL_CLUSTER , REMOTE_CLUSTER_1 )));
431+
432+ EsqlExecutionInfo .Cluster remoteCluster = executionInfo .getCluster (REMOTE_CLUSTER_1 );
433+ assertClusterMetadataSkipped (remoteCluster , overallTookMillis , "logs*" );
434+
435+ EsqlExecutionInfo .Cluster localCluster = executionInfo .getCluster (LOCAL_CLUSTER );
436+ if (count > 1 ) {
437+ assertClusterMetadataNoShards (localCluster , localShards , overallTookMillis , "logs-1" );
438+ } else {
439+ assertClusterMetadataSuccess (localCluster , localShards , overallTookMillis , "logs-1" );
440+ }
441+ }
442+ }
443+
292444 }
293445
294446 protected void populateDateIndex (String clusterAlias , String indexName , int numShards , int numDocs , String date ) {
0 commit comments