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 ;
1516import org .elasticsearch .test .junit .annotations .TestLogging ;
1617import org .elasticsearch .xpack .esql .VerificationException ;
1718
19+ import java .io .IOException ;
1820import java .util .HashSet ;
1921import java .util .List ;
2022import java .util .Map ;
@@ -75,6 +77,17 @@ protected void assertClusterMetadataSkippedShards(
7577 assertThat (clusterMetatata .getFailedShards (), equalTo (0 ));
7678 }
7779
80+ protected void assertClusterMetadataSkipped (EsqlExecutionInfo .Cluster clusterMetatata , long took , String indexExpression ) {
81+ assertThat (clusterMetatata .getIndexExpression (), equalTo (indexExpression ));
82+ assertThat (clusterMetatata .getStatus (), equalTo (EsqlExecutionInfo .Cluster .Status .SKIPPED ));
83+ assertThat (clusterMetatata .getTook ().millis (), greaterThanOrEqualTo (0L ));
84+ assertThat (clusterMetatata .getTook ().millis (), lessThanOrEqualTo (took ));
85+ assertThat (clusterMetatata .getTotalShards (), equalTo (0 ));
86+ assertThat (clusterMetatata .getSuccessfulShards (), equalTo (0 ));
87+ assertThat (clusterMetatata .getSkippedShards (), equalTo (0 ));
88+ assertThat (clusterMetatata .getFailedShards (), equalTo (0 ));
89+ }
90+
7891 protected EsqlQueryResponse runQuery (String query , Boolean ccsMetadataInResponse , QueryBuilder filter ) {
7992 EsqlQueryRequest request = EsqlQueryRequest .syncEsqlQueryRequest ();
8093 request .query (query );
@@ -244,11 +257,12 @@ public void testFilterWithMissingIndex() {
244257 populateDateIndex (LOCAL_CLUSTER , LOCAL_INDEX , localShards , docsTest1 , "2024-11-26" );
245258 populateDateIndex (REMOTE_CLUSTER_1 , REMOTE_INDEX , remoteShards , docsTest2 , "2023-11-26" );
246259
247- int docSize = docsTest1 ;
260+ int count = 0 ;
248261 for (var filter : List .of (
249262 new RangeQueryBuilder ("@timestamp" ).from ("2024-01-01" ).to ("now" ),
250263 new RangeQueryBuilder ("@timestamp" ).from ("2025-01-01" ).to ("now" )
251264 )) {
265+ count ++;
252266 // Local index missing
253267 VerificationException e = expectThrows (
254268 VerificationException .class ,
@@ -264,31 +278,162 @@ public void testFilterWithMissingIndex() {
264278 // e = expectThrows(VerificationException.class, () -> runQuery("from missing,logs-1", randomBoolean(), filter).close());
265279 // assertThat(e.getDetailedMessage(), containsString("Unknown index [missing]"));
266280 // Local index missing + existing remote
267- e = expectThrows (VerificationException .class , () -> runQuery ("from missing,c* :logs-2" , randomBoolean (), filter ).close ());
281+ e = expectThrows (VerificationException .class , () -> runQuery ("from missing,cluster-a :logs-2" , randomBoolean (), filter ).close ());
268282 assertThat (e .getDetailedMessage (), containsString ("Unknown index [missing]" ));
269283 // Wildcard index missing
270284 e = expectThrows (VerificationException .class , () -> runQuery ("from missing*" , randomBoolean (), filter ).close ());
271285 assertThat (e .getDetailedMessage (), containsString ("Unknown index [missing*]" ));
272286 // Wildcard index missing + existing index
273287 try (EsqlQueryResponse resp = runQuery ("from missing*,logs-1" , randomBoolean (), filter )) {
274288 List <List <Object >> values = getValuesList (resp );
275- assertThat (values , hasSize (docSize ));
276- // for the second round
277- docSize = 0 ;
289+ assertThat (values , hasSize (count > 1 ? 0 : docsTest1 ));
278290 }
279291 }
280292 }
281293
282294 public void testFilterWithMissingRemoteIndex () {
283- // TODO
295+ int docsTest1 = 50 ;
296+ int docsTest2 = 30 ;
297+ int localShards = randomIntBetween (1 , 5 );
298+ int remoteShards = randomIntBetween (1 , 5 );
299+ populateDateIndex (LOCAL_CLUSTER , LOCAL_INDEX , localShards , docsTest1 , "2024-11-26" );
300+ populateDateIndex (REMOTE_CLUSTER_1 , REMOTE_INDEX , remoteShards , docsTest2 , "2023-11-26" );
301+
302+ int count = 0 ;
303+ for (var filter : List .of (
304+ new RangeQueryBuilder ("@timestamp" ).from ("2023-01-01" ).to ("now" ),
305+ new RangeQueryBuilder ("@timestamp" ).from ("2024-01-01" ).to ("now" ),
306+ new RangeQueryBuilder ("@timestamp" ).from ("2025-01-01" ).to ("now" )
307+ )) {
308+ count ++;
309+ // Local index missing
310+ VerificationException e = expectThrows (
311+ VerificationException .class ,
312+ () -> runQuery ("from cluster-a:missing" , randomBoolean (), filter ).close ()
313+ );
314+ assertThat (e .getDetailedMessage (), containsString ("Unknown index [cluster-a:missing]" ));
315+ // Local index missing + wildcards
316+ // FIXME: planner does not catch this now
317+ // e = expectThrows(VerificationException.class, () -> runQuery("from cluster-a:missing,cluster-a:logs*", randomBoolean(),
318+ // filter).close());
319+ // assertThat(e.getDetailedMessage(), containsString("Unknown index [cluster-a:missing]"));
320+ // Local index missing + existing index
321+ // FIXME: planner does not catch this now
322+ // e = expectThrows(VerificationException.class, () -> runQuery("from cluster-a:missing,cluster-a:logs-2", randomBoolean(),
323+ // filter).close());
324+ // assertThat(e.getDetailedMessage(), containsString("Unknown index [cluster-a:missing]"));
325+ // Local index + missing remote
326+ e = expectThrows (VerificationException .class , () -> runQuery ("from logs-1,cluster-a:missing" , randomBoolean (), filter ).close ());
327+ assertThat (e .getDetailedMessage (), containsString ("Unknown index [cluster-a:missing]" ));
328+ // Wildcard index missing
329+ e = expectThrows (VerificationException .class , () -> runQuery ("from cluster-a:missing*" , randomBoolean (), filter ).close ());
330+ assertThat (e .getDetailedMessage (), containsString ("Unknown index [cluster-a:missing*]" ));
331+ // Wildcard index missing + existing remote index
332+ try (EsqlQueryResponse resp = runQuery ("from cluster-a:missing*,cluster-a:logs-2" , randomBoolean (), filter )) {
333+ List <List <Object >> values = getValuesList (resp );
334+ assertThat (values , hasSize (count > 1 ? 0 : docsTest2 ));
335+ }
336+ // Wildcard index missing + existing local index
337+ try (EsqlQueryResponse resp = runQuery ("from cluster-a:missing*,logs-1" , randomBoolean (), filter )) {
338+ List <List <Object >> values = getValuesList (resp );
339+ assertThat (values , hasSize (count > 2 ? 0 : docsTest1 ));
340+ }
341+ }
284342 }
285343
286- public void testFilterWithUnavailableRemote () {
287- // TODO
344+ public void testFilterWithUnavailableRemote () throws IOException {
345+ int docsTest1 = 50 ;
346+ int localShards = randomIntBetween (1 , 5 );
347+ populateDateIndex (LOCAL_CLUSTER , LOCAL_INDEX , localShards , docsTest1 , "2024-11-26" );
348+ cluster (REMOTE_CLUSTER_1 ).close ();
349+
350+ for (var filter : List .of (
351+ new RangeQueryBuilder ("@timestamp" ).from ("2024-01-01" ).to ("now" ),
352+ new RangeQueryBuilder ("@timestamp" ).from ("2025-01-01" ).to ("now" )
353+ )) {
354+ // One index
355+ var e = expectThrows (ElasticsearchException .class , () -> runQuery ("from cluster-a:log-2" , randomBoolean (), filter ).close ());
356+ // Two indices
357+ e = expectThrows (ElasticsearchException .class , () -> runQuery ("from logs-1,cluster-a:log-2" , randomBoolean (), filter ).close ());
358+ // Wildcard
359+ e = expectThrows (ElasticsearchException .class , () -> runQuery ("from logs-1,cluster-a:log*" , randomBoolean (), filter ).close ());
360+ }
288361 }
289362
290- public void testFilterWithUnavailableRemoteAndSkipUnavailable () {
291- // TODO
363+ public void testFilterWithUnavailableRemoteAndSkipUnavailable () throws IOException {
364+ setSkipUnavailable (REMOTE_CLUSTER_1 , true );
365+ int docsTest1 = 50 ;
366+ int localShards = randomIntBetween (1 , 5 );
367+ populateDateIndex (LOCAL_CLUSTER , LOCAL_INDEX , localShards , docsTest1 , "2024-11-26" );
368+ cluster (REMOTE_CLUSTER_1 ).close ();
369+ int count = 0 ;
370+
371+ for (var filter : List .of (
372+ new RangeQueryBuilder ("@timestamp" ).from ("2024-01-01" ).to ("now" ),
373+ new RangeQueryBuilder ("@timestamp" ).from ("2025-01-01" ).to ("now" )
374+ )) {
375+ count ++;
376+ // One index
377+ try (EsqlQueryResponse resp = runQuery ("from cluster-a:logs-2" , randomBoolean (), filter )) {
378+ List <List <Object >> values = getValuesList (resp );
379+ assertThat (values , hasSize (0 ));
380+ EsqlExecutionInfo executionInfo = resp .getExecutionInfo ();
381+ assertNotNull (executionInfo );
382+ assertThat (executionInfo .isCrossClusterSearch (), is (true ));
383+ long overallTookMillis = executionInfo .overallTook ().millis ();
384+ assertThat (overallTookMillis , greaterThanOrEqualTo (0L ));
385+
386+ assertThat (executionInfo .clusterAliases (), equalTo (Set .of (REMOTE_CLUSTER_1 )));
387+
388+ EsqlExecutionInfo .Cluster remoteCluster = executionInfo .getCluster (REMOTE_CLUSTER_1 );
389+ assertClusterMetadataSkipped (remoteCluster , overallTookMillis , "logs-2" );
390+ }
391+ // Two indices
392+ try (EsqlQueryResponse resp = runQuery ("from logs-1,cluster-a:logs-2" , randomBoolean (), filter )) {
393+ List <List <Object >> values = getValuesList (resp );
394+ assertThat (values , hasSize (count > 1 ? 0 : docsTest1 ));
395+ EsqlExecutionInfo executionInfo = resp .getExecutionInfo ();
396+ assertNotNull (executionInfo );
397+ assertThat (executionInfo .isCrossClusterSearch (), is (true ));
398+ long overallTookMillis = executionInfo .overallTook ().millis ();
399+ assertThat (overallTookMillis , greaterThanOrEqualTo (0L ));
400+
401+ assertThat (executionInfo .clusterAliases (), equalTo (Set .of (LOCAL_CLUSTER , REMOTE_CLUSTER_1 )));
402+
403+ EsqlExecutionInfo .Cluster remoteCluster = executionInfo .getCluster (REMOTE_CLUSTER_1 );
404+ assertClusterMetadataSkipped (remoteCluster , overallTookMillis , "logs-2" );
405+
406+ EsqlExecutionInfo .Cluster localCluster = executionInfo .getCluster (LOCAL_CLUSTER );
407+ if (count > 1 ) {
408+ assertClusterMetadataNoShards (localCluster , localShards , overallTookMillis , "logs-1" );
409+ } else {
410+ assertClusterMetadataSuccess (localCluster , localShards , overallTookMillis , "logs-1" );
411+ }
412+ }
413+ // Wildcard
414+ try (EsqlQueryResponse resp = runQuery ("from logs-1,cluster-a:logs*" , randomBoolean (), filter )) {
415+ List <List <Object >> values = getValuesList (resp );
416+ assertThat (values , hasSize (count > 1 ? 0 : docsTest1 ));
417+ EsqlExecutionInfo executionInfo = resp .getExecutionInfo ();
418+ assertNotNull (executionInfo );
419+ assertThat (executionInfo .isCrossClusterSearch (), is (true ));
420+ long overallTookMillis = executionInfo .overallTook ().millis ();
421+ assertThat (overallTookMillis , greaterThanOrEqualTo (0L ));
422+
423+ assertThat (executionInfo .clusterAliases (), equalTo (Set .of (LOCAL_CLUSTER , REMOTE_CLUSTER_1 )));
424+
425+ EsqlExecutionInfo .Cluster remoteCluster = executionInfo .getCluster (REMOTE_CLUSTER_1 );
426+ assertClusterMetadataSkipped (remoteCluster , overallTookMillis , "logs*" );
427+
428+ EsqlExecutionInfo .Cluster localCluster = executionInfo .getCluster (LOCAL_CLUSTER );
429+ if (count > 1 ) {
430+ assertClusterMetadataNoShards (localCluster , localShards , overallTookMillis , "logs-1" );
431+ } else {
432+ assertClusterMetadataSuccess (localCluster , localShards , overallTookMillis , "logs-1" );
433+ }
434+ }
435+ }
436+
292437 }
293438
294439 protected void populateDateIndex (String clusterAlias , String indexName , int numShards , int numDocs , String date ) {
0 commit comments