Add index name to cluster block exception (#41489)

Updates the error message to reveal the index name that is causing it.

Closes #40870
This commit is contained in:
Hicham Mallah 2019-05-05 02:10:29 +03:00 committed by Nhat Nguyen
parent 793f13c8b6
commit 4a88da70c5
5 changed files with 82 additions and 41 deletions

View File

@ -25,17 +25,26 @@ import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.rest.RestStatus;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import static java.util.Collections.unmodifiableSet;
public class ClusterBlockException extends ElasticsearchException {
private final Set<ClusterBlock> blocks;
public ClusterBlockException(Set<ClusterBlock> blocks) {
super(buildMessage(blocks));
this.blocks = blocks;
public ClusterBlockException(Set<ClusterBlock> globalLevelBlocks) {
super(buildMessageForGlobalBlocks(globalLevelBlocks));
this.blocks = globalLevelBlocks;
}
public ClusterBlockException(Map<String, Set<ClusterBlock>> indexLevelBlocks) {
super(buildMessageForIndexBlocks(indexLevelBlocks));
this.blocks = indexLevelBlocks.values().stream().flatMap(Collection::stream).collect(Collectors.toSet());
}
public ClusterBlockException(StreamInput in) throws IOException {
@ -74,10 +83,26 @@ public class ClusterBlockException extends ElasticsearchException {
return blocks;
}
private static String buildMessage(Set<ClusterBlock> blocks) {
StringBuilder sb = new StringBuilder("blocked by: ");
for (ClusterBlock block : blocks) {
sb.append("[").append(block.status()).append("/").append(block.id()).append("/").append(block.description()).append("];");
private static String buildMessageForGlobalBlocks(Set<ClusterBlock> globalLevelBlocks) {
assert globalLevelBlocks.isEmpty() == false;
Function<ClusterBlock, String> blockDescription = block -> block.status() + "/" + block.id() + "/" + block.description();
StringBuilder sb = new StringBuilder();
if (globalLevelBlocks.isEmpty() == false) {
sb.append("blocked by: [");
sb.append(globalLevelBlocks.stream().map(blockDescription).collect(Collectors.joining(", ")));
sb.append("];");
}
return sb.toString();
}
private static String buildMessageForIndexBlocks(Map<String, Set<ClusterBlock>> indexLevelBlocks) {
assert indexLevelBlocks.isEmpty() == false;
Function<ClusterBlock, String> blockDescription = block -> block.status() + "/" + block.id() + "/" + block.description();
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, Set<ClusterBlock>> entry : indexLevelBlocks.entrySet()) {
sb.append("index [" + entry.getKey() + "] blocked by: [");
sb.append(entry.getValue().stream().map(blockDescription).collect(Collectors.joining(", ")));
sb.append("];");
}
return sb.toString();
}

View File

@ -18,7 +18,6 @@
*/
package org.elasticsearch.cluster.block;
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
import org.elasticsearch.cluster.AbstractDiffable;
import org.elasticsearch.cluster.Diff;
@ -28,6 +27,7 @@ import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.rest.RestStatus;
import java.io.IOException;
@ -36,14 +36,11 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
import static java.util.Collections.emptySet;
import static java.util.Collections.unmodifiableSet;
import static java.util.stream.Collectors.toSet;
import static java.util.stream.Stream.concat;
/**
* Represents current cluster level blocks to block dirty operations done against the cluster.
@ -83,8 +80,9 @@ public class ClusterBlocks extends AbstractDiffable<ClusterBlocks> {
return indices(level).getOrDefault(index, emptySet());
}
private static EnumMap<ClusterBlockLevel, ImmutableLevelHolder> generateLevelHolders(Set<ClusterBlock> global,
ImmutableOpenMap<String, Set<ClusterBlock>> indicesBlocks) {
private static EnumMap<ClusterBlockLevel, ImmutableLevelHolder> generateLevelHolders(
Set<ClusterBlock> global, ImmutableOpenMap<String, Set<ClusterBlock>> indicesBlocks) {
EnumMap<ClusterBlockLevel, ImmutableLevelHolder> levelHolders = new EnumMap<>(ClusterBlockLevel.class);
for (final ClusterBlockLevel level : ClusterBlockLevel.values()) {
Predicate<ClusterBlock> containsLevel = block -> block.contains(level);
@ -199,13 +197,7 @@ public class ClusterBlocks extends AbstractDiffable<ClusterBlocks> {
}
public ClusterBlockException indexBlockedException(ClusterBlockLevel level, String index) {
if (!indexBlocked(level, index)) {
return null;
}
Stream<ClusterBlock> blocks = concat(
global(level).stream(),
blocksForIndex(level, index).stream());
return new ClusterBlockException(unmodifiableSet(blocks.collect(toSet())));
return indicesBlockedException(level, new String[]{index});
}
public boolean indexBlocked(ClusterBlockLevel level, String index) {
@ -213,20 +205,21 @@ public class ClusterBlocks extends AbstractDiffable<ClusterBlocks> {
}
public ClusterBlockException indicesBlockedException(ClusterBlockLevel level, String[] indices) {
boolean indexIsBlocked = false;
Set<ClusterBlock> globalLevelBlocks = global(level);
Map<String, Set<ClusterBlock>> indexLevelBlocks = new HashMap<>();
for (String index : indices) {
if (indexBlocked(level, index)) {
indexIsBlocked = true;
Set<ClusterBlock> indexBlocks = blocksForIndex(level, index);
if (indexBlocks.isEmpty() == false || globalLevelBlocks.isEmpty() == false) {
indexLevelBlocks.put(index, Sets.union(indexBlocks, globalLevelBlocks));
}
}
if (globalBlocked(level) == false && indexIsBlocked == false) {
if (indexLevelBlocks.isEmpty()) {
if(globalLevelBlocks.isEmpty() == false){
return new ClusterBlockException(globalLevelBlocks);
}
return null;
}
Function<String, Stream<ClusterBlock>> blocksForIndexAtLevel = index -> blocksForIndex(level, index).stream();
Stream<ClusterBlock> blocks = concat(
global(level).stream(),
Stream.of(indices).flatMap(blocksForIndexAtLevel));
return new ClusterBlockException(unmodifiableSet(blocks.collect(toSet())));
return new ClusterBlockException(indexLevelBlocks);
}
/**
@ -235,20 +228,27 @@ public class ClusterBlocks extends AbstractDiffable<ClusterBlocks> {
* like the deletion of an index to free up resources on nodes.
* @param indices the indices to check
*/
public ClusterBlockException indicesAllowReleaseResources(String[] indices) {
final Function<String, Stream<ClusterBlock>> blocksForIndexAtLevel = index ->
blocksForIndex(ClusterBlockLevel.METADATA_WRITE, index).stream();
Stream<ClusterBlock> blocks = concat(
global(ClusterBlockLevel.METADATA_WRITE).stream(),
Stream.of(indices).flatMap(blocksForIndexAtLevel)).filter(clusterBlock -> clusterBlock.isAllowReleaseResources() == false);
Set<ClusterBlock> clusterBlocks = unmodifiableSet(blocks.collect(toSet()));
if (clusterBlocks.isEmpty()) {
Set<ClusterBlock> globalBlocks = global(ClusterBlockLevel.METADATA_WRITE).stream()
.filter(clusterBlock -> clusterBlock.isAllowReleaseResources() == false).collect(toSet());
Map<String, Set<ClusterBlock>> indexLevelBlocks = new HashMap<>();
for (String index : indices) {
Set<ClusterBlock> blocks = Sets.union(globalBlocks, blocksForIndex(ClusterBlockLevel.METADATA_WRITE, index))
.stream().filter(clusterBlock -> clusterBlock.isAllowReleaseResources() == false).collect(toSet());
if (blocks.isEmpty() == false) {
indexLevelBlocks.put(index, Sets.union(globalBlocks, blocks));
}
}
if (indexLevelBlocks.isEmpty()) {
if(globalBlocks.isEmpty() == false){
return new ClusterBlockException(globalBlocks);
}
return null;
}
return new ClusterBlockException(clusterBlocks);
return new ClusterBlockException(indexLevelBlocks);
}
@Override
public String toString() {
if (global.isEmpty() && indices().isEmpty()) {

View File

@ -20,6 +20,7 @@
package org.elasticsearch.action.admin.indices.delete;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.cluster.block.ClusterBlockException;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.settings.Settings;
@ -63,7 +64,22 @@ public class DeleteIndexBlocksIT extends ESIntegTestCase {
}
}
public void testDeleteIndexOnReadOnlyAllowDeleteSetting() {
public void testClusterBlockMessageHasIndexName() {
try {
createIndex("test");
ensureGreen("test");
Settings settings = Settings.builder().put(IndexMetaData.SETTING_READ_ONLY_ALLOW_DELETE, true).build();
client().admin().indices().prepareUpdateSettings("test").setSettings(settings).get();
ClusterBlockException e = expectThrows(ClusterBlockException.class, () ->
client().prepareIndex().setIndex("test").setType("doc").setId("1").setSource("foo", "bar").get());
assertEquals("index [test] blocked by: [FORBIDDEN/12/index read-only / allow delete (api)];", e.getMessage());
} finally {
assertAcked(client().admin().indices().prepareUpdateSettings("test")
.setSettings(Settings.builder().putNull(IndexMetaData.SETTING_READ_ONLY_ALLOW_DELETE).build()).get());
}
}
public void testDeleteIndexOnClusterReadOnlyAllowDeleteSetting() {
createIndex("test");
ensureGreen("test");
client().prepareIndex().setIndex("test").setType("doc").setId("1").setSource("foo", "bar").get();

View File

@ -287,7 +287,7 @@ public class TransportBroadcastByNodeActionTests extends ESTestCase {
action.new AsyncAction(null, request, listener).start();
fail("expected ClusterBlockException");
} catch (ClusterBlockException expected) {
assertEquals("blocked by: [SERVICE_UNAVAILABLE/1/test-block];", expected.getMessage());
assertEquals("index [" + TEST_INDEX + "] blocked by: [SERVICE_UNAVAILABLE/1/test-block];", expected.getMessage());
}
}

View File

@ -620,7 +620,7 @@ public class IndexFollowingIT extends CcrIntegTestCase {
assertThat(response.getStatsResponses().get(0).status().readExceptions().size(), equalTo(1));
ElasticsearchException exception = response.getStatsResponses().get(0).status()
.readExceptions().entrySet().iterator().next().getValue().v2();
assertThat(exception.getRootCause().getMessage(), equalTo("blocked by: [FORBIDDEN/4/index closed];"));
assertThat(exception.getRootCause().getMessage(), equalTo("index [index1] blocked by: [FORBIDDEN/4/index closed];"));
});
leaderClient().admin().indices().open(new OpenIndexRequest("index1")).actionGet();