Skip to content

[8.x] Fix missing index exception handling (#126738) #127301

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@

import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.client.internal.Client;
import org.elasticsearch.cluster.RemoteException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.test.transport.MockTransportService;
Expand Down Expand Up @@ -275,13 +277,13 @@ public void testFilterWithMissingIndex() {
);
assertThat(e.getDetailedMessage(), containsString("Unknown index [missing]"));
// Local index missing + wildcards
// FIXME: planner does not catch this now
// e = expectThrows(VerificationException.class, () -> runQuery("from missing,logs*", randomBoolean(), filter).close());
// assertThat(e.getDetailedMessage(), containsString("Unknown index [missing]"));
// FIXME: planner does not catch this now, it should be VerificationException but for now it's runtime IndexNotFoundException
var ie = expectThrows(IndexNotFoundException.class, () -> runQuery("from missing,logs*", randomBoolean(), filter).close());
assertThat(ie.getDetailedMessage(), containsString("no such index [missing]"));
// Local index missing + existing index
// FIXME: planner does not catch this now
// e = expectThrows(VerificationException.class, () -> runQuery("from missing,logs-1", randomBoolean(), filter).close());
// assertThat(e.getDetailedMessage(), containsString("Unknown index [missing]"));
// FIXME: planner does not catch this now, it should be VerificationException but for now it's runtime IndexNotFoundException
ie = expectThrows(IndexNotFoundException.class, () -> runQuery("from missing,logs-1", randomBoolean(), filter).close());
assertThat(ie.getDetailedMessage(), containsString("no such index [missing]"));
// Local index missing + existing remote
e = expectThrows(VerificationException.class, () -> runQuery("from missing,cluster-a:logs-2", randomBoolean(), filter).close());
assertThat(e.getDetailedMessage(), containsString("Unknown index [missing]"));
Expand Down Expand Up @@ -318,15 +320,19 @@ public void testFilterWithMissingRemoteIndex() {
);
assertThat(e.getDetailedMessage(), containsString("Unknown index [cluster-a:missing]"));
// Local index missing + wildcards
// FIXME: planner does not catch this now
// e = expectThrows(VerificationException.class, () -> runQuery("from cluster-a:missing,cluster-a:logs*", randomBoolean(),
// filter).close());
// assertThat(e.getDetailedMessage(), containsString("Unknown index [cluster-a:missing]"));
// FIXME: planner does not catch this now, it should be VerificationException but for now it's runtime RemoteException
var ie = expectThrows(
RemoteException.class,
() -> runQuery("from cluster-a:missing,cluster-a:logs*", randomBoolean(), filter).close()
);
assertThat(ie.getDetailedMessage(), containsString("no such index [missing]"));
// Local index missing + existing index
// FIXME: planner does not catch this now
// e = expectThrows(VerificationException.class, () -> runQuery("from cluster-a:missing,cluster-a:logs-2", randomBoolean(),
// filter).close());
// assertThat(e.getDetailedMessage(), containsString("Unknown index [cluster-a:missing]"));
// FIXME: planner does not catch this now, it should be VerificationException but for now it's runtime RemoteException
ie = expectThrows(
RemoteException.class,
() -> runQuery("from cluster-a:missing,cluster-a:logs-2", randomBoolean(), filter).close()
);
assertThat(ie.getDetailedMessage(), containsString("no such index [missing]"));
// Local index + missing remote
e = expectThrows(VerificationException.class, () -> runQuery("from logs-1,cluster-a:missing", randomBoolean(), filter).close());
assertThat(e.getDetailedMessage(), containsString("Unknown index [cluster-a:missing]"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,34 @@ public void testOneRemoteClusterPartial() throws Exception {
}
}

public void testLocalIndexMissing() throws Exception {
populateIndices();
EsqlQueryRequest request = new EsqlQueryRequest();
request.query("FROM ok-local,no_such_index | LIMIT 1");
request.includeCCSMetadata(randomBoolean());
for (boolean allowPartial : Set.of(true, false)) {
request.allowPartialResults(allowPartial);
Exception error = expectThrows(Exception.class, () -> runQuery(request).close());
error = EsqlTestUtils.unwrapIfWrappedInRemoteException(error);
assertThat(error.getMessage(), containsString("no such index"));
assertThat(error.getMessage(), containsString("[no_such_index]"));
}
}

public void testRemoteIndexMissing() throws Exception {
populateIndices();
EsqlQueryRequest request = new EsqlQueryRequest();
request.query("FROM cluster-a:ok-cluster1,cluster-a:no_such_index | LIMIT 1");
request.includeCCSMetadata(randomBoolean());
for (boolean allowPartial : Set.of(true, false)) {
request.allowPartialResults(allowPartial);
Exception error = expectThrows(Exception.class, () -> runQuery(request).close());
error = EsqlTestUtils.unwrapIfWrappedInRemoteException(error);
assertThat(error.getMessage(), containsString("no such index"));
assertThat(error.getMessage(), containsString("[no_such_index]"));
}
}

public void testFailToReceiveClusterResponse() throws Exception {
populateIndices();
Exception simulatedFailure = randomFailure();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,32 +138,6 @@ public void testFailed() throws Exception {
assertThat(telemetry.getByRemoteCluster().size(), equalTo(0));
}

// TODO: enable when skip-un patch is merged
// public void testSkipAllRemotes() throws Exception {
// var telemetry = getTelemetryFromQuery("from logs-*,c*:no_such_index | stats sum (v)", "unknown");
//
// assertThat(telemetry.getTotalCount(), equalTo(1L));
// assertThat(telemetry.getSuccessCount(), equalTo(1L));
// assertThat(telemetry.getFailureReasons().size(), equalTo(0));
// assertThat(telemetry.getTook().count(), equalTo(1L));
// assertThat(telemetry.getTookMrtFalse().count(), equalTo(0L));
// assertThat(telemetry.getTookMrtTrue().count(), equalTo(0L));
// assertThat(telemetry.getRemotesPerSearchAvg(), equalTo(2.0));
// assertThat(telemetry.getRemotesPerSearchMax(), equalTo(2L));
// assertThat(telemetry.getSearchCountWithSkippedRemotes(), equalTo(1L));
// assertThat(telemetry.getClientCounts().size(), equalTo(0));
//
// var perCluster = telemetry.getByRemoteCluster();
// assertThat(perCluster.size(), equalTo(3));
// for (String clusterAlias : remoteClusterAlias()) {
// var clusterData = perCluster.get(clusterAlias);
// assertThat(clusterData.getCount(), equalTo(0L));
// assertThat(clusterData.getSkippedCount(), equalTo(1L));
// assertThat(clusterData.getTook().count(), equalTo(0L));
// }
// assertPerClusterCount(perCluster.get(LOCAL_CLUSTER), 1L);
// }

public void testRemoteOnly() throws Exception {
setupClusters();
var telemetry = getTelemetryFromQuery("from c*:logs-* | stats sum (v)", "kibana");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

package org.elasticsearch.xpack.esql.plugin;

import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionListenerResponseHandler;
import org.elasticsearch.action.OriginalIndices;
Expand All @@ -16,6 +17,7 @@
import org.elasticsearch.compute.operator.exchange.ExchangeSourceHandler;
import org.elasticsearch.core.Releasable;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.tasks.CancellableTask;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.tasks.TaskCancelledException;
Expand Down Expand Up @@ -88,12 +90,18 @@ void startComputeOnRemoteCluster(
if (receivedResults == false && EsqlCCSUtils.shouldIgnoreRuntimeError(executionInfo, clusterAlias, e)) {
EsqlCCSUtils.markClusterWithFinalStateAndNoShards(executionInfo, clusterAlias, EsqlExecutionInfo.Cluster.Status.SKIPPED, e);
l.onResponse(List.of());
} else if (configuration.allowPartialResults()) {
EsqlCCSUtils.markClusterWithFinalStateAndNoShards(executionInfo, clusterAlias, EsqlExecutionInfo.Cluster.Status.PARTIAL, e);
l.onResponse(List.of());
} else {
l.onFailure(e);
}
} else if (configuration.allowPartialResults()
&& (ExceptionsHelper.unwrapCause(e) instanceof IndexNotFoundException) == false) {
EsqlCCSUtils.markClusterWithFinalStateAndNoShards(
executionInfo,
clusterAlias,
EsqlExecutionInfo.Cluster.Status.PARTIAL,
e
);
l.onResponse(List.of());
} else {
l.onFailure(e);
}
});
ExchangeService.openExchange(
transportService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

package org.elasticsearch.xpack.esql.plugin;

import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.OriginalIndices;
import org.elasticsearch.action.search.SearchRequest;
Expand All @@ -27,6 +28,7 @@
import org.elasticsearch.core.Releasables;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.core.Tuple;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.index.query.SearchExecutionContext;
import org.elasticsearch.logging.LogManager;
import org.elasticsearch.logging.Logger;
Expand Down Expand Up @@ -274,7 +276,8 @@ public void execute(
);
dataNodesListener.onResponse(r.getProfiles());
}, e -> {
if (configuration.allowPartialResults()) {
if (configuration.allowPartialResults()
&& (ExceptionsHelper.unwrapCause(e) instanceof IndexNotFoundException) == false) {
execInfo.swapCluster(
LOCAL_CLUSTER,
(k, v) -> new EsqlExecutionInfo.Cluster.Builder(v).setStatus(
Expand Down