diff --git a/server/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java b/server/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java index dde4b81af94d2..fb41f4d298990 100644 --- a/server/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java +++ b/server/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java @@ -27,6 +27,7 @@ import org.elasticsearch.action.support.SubscribableListener; import org.elasticsearch.cluster.ProjectState; import org.elasticsearch.cluster.block.ClusterBlockLevel; +import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.project.ProjectResolver; import org.elasticsearch.cluster.service.ClusterService; @@ -171,7 +172,7 @@ private void doExecuteForked( final Map remoteClusterIndices = transportService.getRemoteClusterService() .groupIndices(request.indicesOptions(), request.indices()); final OriginalIndices localIndices = remoteClusterIndices.remove(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY); - final String[] concreteIndices; + String[] concreteIndices; if (localIndices == null) { // in the case we have one or more remote indices but no local we don't expand to all local indices and just do remote indices concreteIndices = Strings.EMPTY_ARRAY; @@ -184,7 +185,7 @@ private void doExecuteForked( return; } - checkIndexBlocks(projectState, concreteIndices); + concreteIndices = checkBlocksAndFilterIndices(projectState, concreteIndices); final FailureCollector indexFailures = new FailureCollector(); final Map indexResponses = new HashMap<>(); // This map is used to share the index response for indices which have the same index mapping hash to reduce the memory usage. @@ -371,17 +372,22 @@ void executeRemoteRequest( ); } - private static void checkIndexBlocks(ProjectState projectState, String[] concreteIndices) { + static String[] checkBlocksAndFilterIndices(ProjectState projectState, String[] concreteIndices) { var blocks = projectState.blocks(); var projectId = projectState.projectId(); if (blocks.global(projectId).isEmpty() && blocks.indices(projectId).isEmpty()) { // short circuit optimization because block check below is relatively expensive for many indices - return; + return concreteIndices; } blocks.globalBlockedRaiseException(projectId, ClusterBlockLevel.READ); + List filteredIndices = new ArrayList<>(concreteIndices.length); for (String index : concreteIndices) { - blocks.indexBlockedRaiseException(projectState.projectId(), ClusterBlockLevel.READ, index); + blocks.indexBlockedRaiseException(projectId, ClusterBlockLevel.READ, index); + if (blocks.hasIndexBlock(projectId, index, IndexMetadata.INDEX_REFRESH_BLOCK) == false) { + filteredIndices.add(index); + } } + return filteredIndices.toArray(new String[0]); } private static void mergeIndexResponses( diff --git a/server/src/test/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesActionTests.java b/server/src/test/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesActionTests.java index b60d3e0f08569..68d12d736b1d5 100644 --- a/server/src/test/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesActionTests.java @@ -13,12 +13,23 @@ import org.elasticsearch.TransportVersions; import org.elasticsearch.action.support.ActionFilter; import org.elasticsearch.action.support.ActionFilters; +import org.elasticsearch.cluster.ClusterName; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.ProjectState; +import org.elasticsearch.cluster.block.ClusterBlock; +import org.elasticsearch.cluster.block.ClusterBlockException; +import org.elasticsearch.cluster.block.ClusterBlockLevel; +import org.elasticsearch.cluster.block.ClusterBlocks; +import org.elasticsearch.cluster.metadata.IndexMetadata; +import org.elasticsearch.cluster.metadata.ProjectId; +import org.elasticsearch.cluster.metadata.ProjectMetadata; import org.elasticsearch.cluster.node.VersionInformation; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.indices.IndicesService; +import org.elasticsearch.rest.RestStatus; import org.elasticsearch.search.DummyQueryBuilder; import org.elasticsearch.search.SearchService; import org.elasticsearch.test.ESTestCase; @@ -29,8 +40,10 @@ import org.elasticsearch.transport.TransportService; import java.io.IOException; +import java.util.EnumSet; import java.util.concurrent.TimeUnit; +import static org.elasticsearch.action.fieldcaps.TransportFieldCapabilitiesAction.checkBlocksAndFilterIndices; import static org.hamcrest.Matchers.containsString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -104,4 +117,63 @@ protected void doWriteTo(StreamOutput out) throws IOException { assertTrue(ESTestCase.terminate(threadPool)); } } + + public void testCheckBlocksAndFilterIndices() { + final ProjectId projectId = randomProjectIdOrDefault(); + String[] concreteIndices = { "index1", "index2", "index3" }; + + // No blocks + { + final ClusterState clusterState = ClusterState.builder(ClusterName.DEFAULT) + .putProjectMetadata(ProjectMetadata.builder(projectId).build()) + .build(); + final ProjectState projectState = clusterState.projectState(projectId); + String[] result = checkBlocksAndFilterIndices(projectState, concreteIndices); + assertArrayEquals(concreteIndices, result); + } + + // Global READ block + { + final ClusterState clusterState = ClusterState.builder(ClusterName.DEFAULT) + .putProjectMetadata(ProjectMetadata.builder(projectId).build()) + .blocks( + ClusterBlocks.builder() + .addGlobalBlock( + new ClusterBlock( + 0, + "id", + false, + false, + false, + RestStatus.SERVICE_UNAVAILABLE, + EnumSet.of(ClusterBlockLevel.READ) + ) + ) + ) + .build(); + final ProjectState projectState = clusterState.projectState(projectId); + expectThrows(ClusterBlockException.class, () -> checkBlocksAndFilterIndices(projectState, concreteIndices)); + } + + // Index-level READ block + { + final ClusterState clusterState = ClusterState.builder(ClusterName.DEFAULT) + .putProjectMetadata(ProjectMetadata.builder(projectId).build()) + .blocks(ClusterBlocks.builder().addIndexBlock(projectId, "index1", IndexMetadata.INDEX_READ_BLOCK)) + .build(); + final ProjectState projectState = clusterState.projectState(projectId); + expectThrows(ClusterBlockException.class, () -> checkBlocksAndFilterIndices(projectState, concreteIndices)); + } + + // Index-level INDEX_REFRESH_BLOCK + { + final ClusterState clusterState = ClusterState.builder(ClusterName.DEFAULT) + .putProjectMetadata(ProjectMetadata.builder(projectId).build()) + .blocks(ClusterBlocks.builder().addIndexBlock(projectId, "index2", IndexMetadata.INDEX_REFRESH_BLOCK)) + .build(); + final ProjectState projectState = clusterState.projectState(projectId); + String[] result = checkBlocksAndFilterIndices(projectState, concreteIndices); + assertArrayEquals(new String[] { "index1", "index3" }, result); + } + } }