Add index cleanup if index creation fails

Fixes #2590
This commit is contained in:
Igor Motov 2013-01-30 10:39:38 -05:00
parent 5c40c97e6e
commit 29f4274213
2 changed files with 46 additions and 3 deletions

View File

@ -134,6 +134,8 @@ public class MetaDataCreateIndexService extends AbstractComponent {
clusterService.submitStateUpdateTask("create-index [" + request.index + "], cause [" + request.cause + "]", new ProcessedClusterStateUpdateTask() { clusterService.submitStateUpdateTask("create-index [" + request.index + "], cause [" + request.cause + "]", new ProcessedClusterStateUpdateTask() {
@Override @Override
public ClusterState execute(ClusterState currentState) { public ClusterState execute(ClusterState currentState) {
boolean indexCreated = false;
String failureReason = null;
try { try {
try { try {
validate(request, currentState); 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 // 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()); indicesService.createIndex(request.index, actualIndexSettings, clusterService.state().nodes().localNode().id());
indexCreated = true;
// now add the mappings // now add the mappings
IndexService indexService = indicesService.indexServiceSafe(request.index); IndexService indexService = indicesService.indexServiceSafe(request.index);
MapperService mapperService = indexService.mapperService(); MapperService mapperService = indexService.mapperService();
@ -266,7 +269,7 @@ public class MetaDataCreateIndexService extends AbstractComponent {
try { try {
mapperService.merge(MapperService.DEFAULT_MAPPING, XContentFactory.jsonBuilder().map(mappings.get(MapperService.DEFAULT_MAPPING)).string(), false); mapperService.merge(MapperService.DEFAULT_MAPPING, XContentFactory.jsonBuilder().map(mappings.get(MapperService.DEFAULT_MAPPING)).string(), false);
} catch (Exception e) { } 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); 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 // apply the default here, its the first time we parse it
mapperService.merge(entry.getKey(), XContentFactory.jsonBuilder().map(entry.getValue()).string(), true); mapperService.merge(entry.getKey(), XContentFactory.jsonBuilder().map(entry.getValue()).string(), true);
} catch (Exception e) { } 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); throw new MapperParsingException("mapping [" + entry.getKey() + "]", e);
} }
} }
@ -297,7 +300,13 @@ public class MetaDataCreateIndexService extends AbstractComponent {
indexMetaDataBuilder.putCustom(customEntry.getKey(), customEntry.getValue()); indexMetaDataBuilder.putCustom(customEntry.getKey(), customEntry.getValue());
} }
indexMetaDataBuilder.state(request.state); 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 newMetaData = newMetaDataBuilder()
.metaData(currentState.metaData()) .metaData(currentState.metaData())
@ -353,6 +362,10 @@ public class MetaDataCreateIndexService extends AbstractComponent {
return updatedState; return updatedState;
} catch (Throwable e) { } catch (Throwable e) {
logger.warn("[{}] failed to create", e, request.index); 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); listener.onFailure(e);
return currentState; return currentState;
} }

View File

@ -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.health.ClusterHealthResponse;
import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse; 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.action.admin.indices.status.IndicesStatusResponse;
import org.elasticsearch.cluster.block.ClusterBlockException; import org.elasticsearch.cluster.block.ClusterBlockException;
import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.routing.ShardRoutingState; import org.elasticsearch.cluster.routing.ShardRoutingState;
import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers; 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.elasticsearch.test.integration.AbstractNodesTests;
import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterMethod;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.nullValue;
@ -101,4 +105,30 @@ public class SimpleIndexStateTests extends AbstractNodesTests {
logger.info("--> indexing a simple document"); logger.info("--> indexing a simple document");
client("node1").prepareIndex("test", "type1", "1").setSource("field1", "value1").execute().actionGet(); 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));
}
} }