From 79b06291cf373838acd0a9c5af886989dc86f4fe Mon Sep 17 00:00:00 2001 From: "ievgen.degtiarenko" Date: Wed, 24 Sep 2025 15:13:50 +0200 Subject: [PATCH 1/2] Simplify returnLocalAll handling in ES|QL --- .../indices/IndicesExpressionGrouper.java | 35 ++----------------- .../transport/RemoteClusterService.java | 6 +--- .../xpack/esql/session/EsqlCCSUtils.java | 14 +++----- .../xpack/esql/session/EsqlSession.java | 5 ++- .../xpack/esql/session/EsqlCCSUtilsTests.java | 6 +--- .../telemetry/PlanExecutorMetricsTests.java | 2 +- 6 files changed, 11 insertions(+), 57 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/indices/IndicesExpressionGrouper.java b/server/src/main/java/org/elasticsearch/indices/IndicesExpressionGrouper.java index 9660c57260529..7f3d1cb6bedbb 100644 --- a/server/src/main/java/org/elasticsearch/indices/IndicesExpressionGrouper.java +++ b/server/src/main/java/org/elasticsearch/indices/IndicesExpressionGrouper.java @@ -11,10 +11,8 @@ import org.elasticsearch.action.OriginalIndices; import org.elasticsearch.action.support.IndicesOptions; -import org.elasticsearch.common.Strings; import java.util.Map; -import java.util.Set; /** * Interface for grouping index expressions, along with IndicesOptions by cluster alias. @@ -30,36 +28,7 @@ public interface IndicesExpressionGrouper { /** - * @param remoteClusterNames Set of configured remote cluster names. - * @param indicesOptions IndicesOptions to clarify how the index expression should be parsed/applied - * @param indexExpressionCsv Multiple index expressions as CSV string (with no spaces), e.g., "logs1,logs2,cluster-a:logs1". - * A single index expression is also supported. - * @return Map where the key is the cluster alias (for "local" cluster, it is RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY) - * and the value for that cluster from the index expression is an OriginalIndices object. + * See {@link org.elasticsearch.transport.RemoteClusterService#groupIndices} for details */ - default Map groupIndices( - Set remoteClusterNames, - IndicesOptions indicesOptions, - String indexExpressionCsv - ) { - return groupIndices(remoteClusterNames, indicesOptions, Strings.splitStringByCommaToArray(indexExpressionCsv)); - } - - /** - * Same behavior as the other groupIndices, except the incoming multiple index expressions must already be - * parsed into a String array. - * @param remoteClusterNames Set of configured remote cluster names. - * @param indicesOptions IndicesOptions to clarify how the index expressions should be parsed/applied - * @param indexExpressions Multiple index expressions as string[]. - * @return Map where the key is the cluster alias (for "local" cluster, it is RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY) - * and the value for that cluster from the index expression is an OriginalIndices object. - */ - Map groupIndices(Set remoteClusterNames, IndicesOptions indicesOptions, String[] indexExpressions); - - /** - * Returns a set of currently configured remote clusters. - */ - default Set getConfiguredClusters() { - return Set.of(); - } + Map groupIndices(IndicesOptions indicesOptions, String[] indexExpressions, boolean returnLocalAll); } diff --git a/server/src/main/java/org/elasticsearch/transport/RemoteClusterService.java b/server/src/main/java/org/elasticsearch/transport/RemoteClusterService.java index d2ad9f95f23e7..419aa189e352c 100644 --- a/server/src/main/java/org/elasticsearch/transport/RemoteClusterService.java +++ b/server/src/main/java/org/elasticsearch/transport/RemoteClusterService.java @@ -168,6 +168,7 @@ public Map groupIndices(Set remoteClusterNames, return groupIndices(remoteClusterNames, indicesOptions, indices, true); } + @Override public Map groupIndices(IndicesOptions indicesOptions, String[] indices, boolean returnLocalAll) { return groupIndices(getRegisteredRemoteClusterNames(), indicesOptions, indices, returnLocalAll); } @@ -176,11 +177,6 @@ public Map groupIndices(IndicesOptions indicesOptions, return groupIndices(getRegisteredRemoteClusterNames(), indicesOptions, indices, true); } - @Override - public Set getConfiguredClusters() { - return getRegisteredRemoteClusterNames(); - } - /** * Returns the registered remote cluster names. */ diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlCCSUtils.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlCCSUtils.java index a1cba3bff0a4e..25be7292ad11c 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlCCSUtils.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlCCSUtils.java @@ -23,7 +23,6 @@ import org.elasticsearch.transport.ConnectTransportException; import org.elasticsearch.transport.NoSuchRemoteClusterException; import org.elasticsearch.transport.RemoteClusterAware; -import org.elasticsearch.transport.RemoteClusterService; import org.elasticsearch.transport.RemoteTransportException; import org.elasticsearch.xpack.esql.VerificationException; import org.elasticsearch.xpack.esql.action.EsqlExecutionInfo; @@ -340,11 +339,9 @@ public static void initCrossClusterState( } try { var groupedIndices = indicesGrouper.groupIndices( - // indicesGrouper.getConfiguredClusters() might return mutable set that changes as clusters connect or disconnect. - // it is copied here so that we have the same resolution when request contains multiple remote cluster patterns with * - Set.copyOf(indicesGrouper.getConfiguredClusters()), IndicesOptions.DEFAULT, - indexPattern.indexPattern() + Strings.splitStringByCommaToArray(indexPattern.indexPattern()), + false ); executionInfo.clusterInfoInitializing(true); @@ -363,11 +360,8 @@ public static void initCrossClusterState( executionInfo.clusterInfoInitializing(false); } - // check if it is a cross-cluster query - if (groupedIndices.size() > 1 || groupedIndices.containsKey(RemoteClusterService.LOCAL_CLUSTER_GROUP_KEY) == false) { - if (EsqlLicenseChecker.isCcsAllowed(licenseState) == false) { - throw EsqlLicenseChecker.invalidLicenseForCcsException(licenseState); - } + if (executionInfo.isCrossClusterSearch() && EsqlLicenseChecker.isCcsAllowed(licenseState) == false) { + throw EsqlLicenseChecker.invalidLicenseForCcsException(licenseState); } } catch (NoSuchRemoteClusterException e) { if (EsqlLicenseChecker.isCcsAllowed(licenseState)) { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlSession.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlSession.java index fa14952bf1b50..53ba78a11ecb6 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlSession.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlSession.java @@ -647,15 +647,14 @@ private void preAnalyzeMainIndices( ThreadPool.Names.SYSTEM_READ ); if (preAnalysis.indexPattern() != null) { - String indexExpressionToResolve = EsqlCCSUtils.createIndexExpressionFromAvailableClusters(executionInfo); - if (indexExpressionToResolve.isEmpty()) { + if (executionInfo.clusterAliases().isEmpty()) { // if this was a pure remote CCS request (no local indices) and all remotes are offline, return an empty IndexResolution listener.onResponse( result.withIndices(IndexResolution.valid(new EsIndex(preAnalysis.indexPattern().indexPattern(), Map.of(), Map.of()))) ); } else { indexResolver.resolveAsMergedMapping( - indexExpressionToResolve, + EsqlCCSUtils.createIndexExpressionFromAvailableClusters(executionInfo), result.fieldNames, // Maybe if no indices are returned, retry without index mode and provide a clearer error message. switch (preAnalysis.indexMode()) { diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/session/EsqlCCSUtilsTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/session/EsqlCCSUtilsTests.java index 76ef94f008628..35b0ef23cdb5d 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/session/EsqlCCSUtilsTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/session/EsqlCCSUtilsTests.java @@ -799,11 +799,7 @@ private XPackLicenseStatus inactiveLicenseStatus(License.OperationMode operation static class TestIndicesExpressionGrouper implements IndicesExpressionGrouper { @Override - public Map groupIndices( - Set remoteClusterNames, - IndicesOptions indicesOptions, - String[] indexExpressions - ) { + public Map groupIndices(IndicesOptions indicesOptions, String[] indexExpressions, boolean returnLocalAll) { final Map originalIndicesMap = new HashMap<>(); final String localKey = RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY; diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/telemetry/PlanExecutorMetricsTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/telemetry/PlanExecutorMetricsTests.java index 752e61c240cd5..c24ae185c5f25 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/telemetry/PlanExecutorMetricsTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/telemetry/PlanExecutorMetricsTests.java @@ -157,7 +157,7 @@ public void testFailedMetric() { // test a failed query: xyz field doesn't exist request.query("from test | stats m = max(xyz)"); EsqlSession.PlanRunner runPhase = (p, r) -> fail("this shouldn't happen"); - IndicesExpressionGrouper groupIndicesByCluster = (remoteClusterNames, indicesOptions, indexExpressions) -> Map.of( + IndicesExpressionGrouper groupIndicesByCluster = (indicesOptions, indexExpressions, returnLocalAll) -> Map.of( "", new OriginalIndices(new String[] { "test" }, IndicesOptions.DEFAULT) ); From 07410b51e040595957002e82e920b5692089caa7 Mon Sep 17 00:00:00 2001 From: "ievgen.degtiarenko" Date: Fri, 26 Sep 2025 08:36:50 +0200 Subject: [PATCH 2/2] upd --- .../org/elasticsearch/xpack/esql/session/EsqlSession.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlSession.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlSession.java index 4a08308579f88..19f72320e9178 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlSession.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlSession.java @@ -688,14 +688,15 @@ private void preAnalyzeMainIndices( ThreadPool.Names.SYSTEM_READ ); if (preAnalysis.indexPattern() != null) { - if (executionInfo.clusterAliases().isEmpty()) { + String indexExpressionToResolve = EsqlCCSUtils.createIndexExpressionFromAvailableClusters(executionInfo); + if (indexExpressionToResolve.isEmpty()) { // if this was a pure remote CCS request (no local indices) and all remotes are offline, return an empty IndexResolution listener.onResponse( result.withIndices(IndexResolution.valid(new EsIndex(preAnalysis.indexPattern().indexPattern(), Map.of(), Map.of()))) ); } else { indexResolver.resolveAsMergedMapping( - EsqlCCSUtils.createIndexExpressionFromAvailableClusters(executionInfo), + indexExpressionToResolve, result.fieldNames, // Maybe if no indices are returned, retry without index mode and provide a clearer error message. switch (preAnalysis.indexMode()) {