diff --git a/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java b/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java index 032b82f95db..ad147a8ae23 100644 --- a/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java +++ b/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java @@ -134,6 +134,8 @@ public class MetaDataCreateIndexService extends AbstractComponent { clusterService.submitStateUpdateTask("create-index [" + request.index + "], cause [" + request.cause + "]", new ProcessedClusterStateUpdateTask() { @Override public ClusterState execute(ClusterState currentState) { + boolean indexCreated = false; + String failureReason = null; try { try { validate(request, currentState); @@ -258,6 +260,7 @@ public class MetaDataCreateIndexService extends AbstractComponent { // create the index here (on the master) to validate it can be created, as well as adding the mapping indicesService.createIndex(request.index, actualIndexSettings, clusterService.state().nodes().localNode().id()); + indexCreated = true; // now add the mappings IndexService indexService = indicesService.indexServiceSafe(request.index); MapperService mapperService = indexService.mapperService(); @@ -266,7 +269,7 @@ public class MetaDataCreateIndexService extends AbstractComponent { try { mapperService.merge(MapperService.DEFAULT_MAPPING, XContentFactory.jsonBuilder().map(mappings.get(MapperService.DEFAULT_MAPPING)).string(), false); } catch (Exception e) { - indicesService.deleteIndex(request.index, "failed on parsing default mapping on index creation"); + failureReason = "failed on parsing default mapping on index creation"; throw new MapperParsingException("mapping [" + MapperService.DEFAULT_MAPPING + "]", e); } } @@ -278,7 +281,7 @@ public class MetaDataCreateIndexService extends AbstractComponent { // apply the default here, its the first time we parse it mapperService.merge(entry.getKey(), XContentFactory.jsonBuilder().map(entry.getValue()).string(), true); } catch (Exception e) { - indicesService.deleteIndex(request.index, "failed on parsing mappings on index creation"); + failureReason = "failed on parsing mappings on index creation"; throw new MapperParsingException("mapping [" + entry.getKey() + "]", e); } } @@ -297,7 +300,13 @@ public class MetaDataCreateIndexService extends AbstractComponent { indexMetaDataBuilder.putCustom(customEntry.getKey(), customEntry.getValue()); } indexMetaDataBuilder.state(request.state); - final IndexMetaData indexMetaData = indexMetaDataBuilder.build(); + final IndexMetaData indexMetaData; + try { + indexMetaData = indexMetaDataBuilder.build(); + } catch (Exception e) { + failureReason = "failed to build index metadata"; + throw e; + } MetaData newMetaData = newMetaDataBuilder() .metaData(currentState.metaData()) @@ -353,6 +362,10 @@ public class MetaDataCreateIndexService extends AbstractComponent { return updatedState; } catch (Throwable e) { logger.warn("[{}] failed to create", e, request.index); + if (indexCreated) { + // Index was already partially created - need to clean up + indicesService.deleteIndex(request.index, failureReason != null ? failureReason : "failed to create index"); + } listener.onFailure(e); return currentState; } diff --git a/src/test/java/org/elasticsearch/test/integration/indices/state/SimpleIndexStateTests.java b/src/test/java/org/elasticsearch/test/integration/indices/state/SimpleIndexStateTests.java index 6b67dbaebb7..d2144d87295 100644 --- a/src/test/java/org/elasticsearch/test/integration/indices/state/SimpleIndexStateTests.java +++ b/src/test/java/org/elasticsearch/test/integration/indices/state/SimpleIndexStateTests.java @@ -21,16 +21,20 @@ package org.elasticsearch.test.integration.indices.state; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse; +import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; import org.elasticsearch.action.admin.indices.status.IndicesStatusResponse; import org.elasticsearch.cluster.block.ClusterBlockException; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.routing.ShardRoutingState; import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.logging.Loggers; +import org.elasticsearch.common.settings.SettingsException; +import org.elasticsearch.indices.IndexMissingException; import org.elasticsearch.test.integration.AbstractNodesTests; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; +import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.nullValue; @@ -101,4 +105,30 @@ public class SimpleIndexStateTests extends AbstractNodesTests { logger.info("--> indexing a simple document"); client("node1").prepareIndex("test", "type1", "1").setSource("field1", "value1").execute().actionGet(); } + + @Test + public void testConsistencyAfterIndexCreationFailure() { + logger.info("--> starting one node...."); + startNode("node1"); + + logger.info("--> deleting test index...."); + try { + client("node1").admin().indices().prepareDelete("test").execute().actionGet(); + } catch (IndexMissingException ex) { + // Ignore + } + + logger.info("--> creating test index with invalid settings "); + try { + client("node1").admin().indices().prepareCreate("test").setSettings(settingsBuilder().put("number_of_shards", "bad")).execute().actionGet(); + assert false; + } catch (SettingsException ex) { + // Expected + } + + logger.info("--> creating test index with valid settings "); + CreateIndexResponse response = client("node1").admin().indices().prepareCreate("test").setSettings(settingsBuilder().put("number_of_shards", 1)).execute().actionGet(); + assertThat(response.acknowledged(), equalTo(true)); + } + }