diff --git a/buildSrc/src/main/resources/checkstyle_suppressions.xml b/buildSrc/src/main/resources/checkstyle_suppressions.xml
index 46ddcb0ad0e07..72e10cb93980c 100644
--- a/buildSrc/src/main/resources/checkstyle_suppressions.xml
+++ b/buildSrc/src/main/resources/checkstyle_suppressions.xml
@@ -419,7 +419,6 @@
-
diff --git a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexStateService.java b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexStateService.java
index 71962d6356a3a..e6e7084e4d96e 100644
--- a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexStateService.java
+++ b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexStateService.java
@@ -19,6 +19,7 @@
package org.elasticsearch.cluster.metadata;
+import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.indices.close.CloseIndexClusterStateUpdateRequest;
import org.elasticsearch.action.admin.indices.open.OpenIndexClusterStateUpdateRequest;
@@ -37,6 +38,8 @@
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.Index;
+import org.elasticsearch.index.NodeServicesProvider;
+import org.elasticsearch.indices.IndicesService;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.snapshots.RestoreService;
import org.elasticsearch.snapshots.SnapshotsService;
@@ -59,10 +62,16 @@ public class MetaDataIndexStateService extends AbstractComponent {
private final AllocationService allocationService;
private final MetaDataIndexUpgradeService metaDataIndexUpgradeService;
+ private final NodeServicesProvider nodeServiceProvider;
+ private final IndicesService indicesService;
@Inject
- public MetaDataIndexStateService(Settings settings, ClusterService clusterService, AllocationService allocationService, MetaDataIndexUpgradeService metaDataIndexUpgradeService) {
+ public MetaDataIndexStateService(Settings settings, ClusterService clusterService, AllocationService allocationService,
+ MetaDataIndexUpgradeService metaDataIndexUpgradeService,
+ NodeServicesProvider nodeServicesProvider, IndicesService indicesService) {
super(settings);
+ this.nodeServiceProvider = nodeServicesProvider;
+ this.indicesService = indicesService;
this.clusterService = clusterService;
this.allocationService = allocationService;
this.metaDataIndexUpgradeService = metaDataIndexUpgradeService;
@@ -162,6 +171,12 @@ public ClusterState execute(ClusterState currentState) {
// The index might be closed because we couldn't import it due to old incompatible version
// We need to check that this index can be upgraded to the current version
indexMetaData = metaDataIndexUpgradeService.upgradeIndexMetaData(indexMetaData);
+ try {
+ indicesService.verifyIndexMetadata(nodeServiceProvider, indexMetaData);
+ } catch (Exception e) {
+ throw new ElasticsearchException("Failed to verify index " + indexMetaData.getIndex(), e);
+ }
+
mdBuilder.put(indexMetaData, true);
blocksBuilder.removeIndexBlock(indexName, INDEX_CLOSED_BLOCK);
}
diff --git a/core/src/main/java/org/elasticsearch/gateway/Gateway.java b/core/src/main/java/org/elasticsearch/gateway/Gateway.java
index f5d38112c4fd7..54c270e207215 100644
--- a/core/src/main/java/org/elasticsearch/gateway/Gateway.java
+++ b/core/src/main/java/org/elasticsearch/gateway/Gateway.java
@@ -35,8 +35,14 @@
import org.elasticsearch.discovery.Discovery;
import org.elasticsearch.env.NodeEnvironment;
import org.elasticsearch.index.Index;
+import org.elasticsearch.index.IndexService;
+import org.elasticsearch.index.NodeServicesProvider;
+import org.elasticsearch.indices.IndicesService;
+import java.io.IOException;
import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.function.Supplier;
/**
@@ -53,10 +59,15 @@ public class Gateway extends AbstractComponent implements ClusterStateListener {
private final TransportNodesListGatewayMetaState listGatewayMetaState;
private final Supplier minimumMasterNodesProvider;
+ private final IndicesService indicesService;
+ private final NodeServicesProvider nodeServicesProvider;
public Gateway(Settings settings, ClusterService clusterService, NodeEnvironment nodeEnv, GatewayMetaState metaState,
- TransportNodesListGatewayMetaState listGatewayMetaState, Discovery discovery) {
+ TransportNodesListGatewayMetaState listGatewayMetaState, Discovery discovery,
+ NodeServicesProvider nodeServicesProvider, IndicesService indicesService) {
super(settings);
+ this.nodeServicesProvider = nodeServicesProvider;
+ this.indicesService = indicesService;
this.clusterService = clusterService;
this.nodeEnv = nodeEnv;
this.metaState = metaState;
@@ -66,9 +77,9 @@ public Gateway(Settings settings, ClusterService clusterService, NodeEnvironment
}
public void performStateRecovery(final GatewayStateRecoveredListener listener) throws GatewayException {
- ObjectHashSet nodesIds = new ObjectHashSet<>(clusterService.state().nodes().masterNodes().keys());
- logger.trace("performing state recovery from {}", nodesIds);
- TransportNodesListGatewayMetaState.NodesGatewayMetaState nodesState = listGatewayMetaState.list(nodesIds.toArray(String.class), null).actionGet();
+ String[] nodesIds = clusterService.state().nodes().masterNodes().keys().toArray(String.class);
+ logger.trace("performing state recovery from {}", Arrays.toString(nodesIds));
+ TransportNodesListGatewayMetaState.NodesGatewayMetaState nodesState = listGatewayMetaState.list(nodesIds, null).actionGet();
int requiredAllocation = Math.max(1, minimumMasterNodesProvider.get());
@@ -129,7 +140,17 @@ public void performStateRecovery(final GatewayStateRecoveredListener listener) t
if (electedIndexMetaData != null) {
if (indexMetaDataCount < requiredAllocation) {
logger.debug("[{}] found [{}], required [{}], not adding", index, indexMetaDataCount, requiredAllocation);
+ } // TODO if this logging statement is correct then we are missing an else here
+ try {
+ if (electedIndexMetaData.getState() == IndexMetaData.State.OPEN) {
+ // verify that we can actually create this index - if not we recover it as closed with lots of warn logs
+ indicesService.verifyIndexMetadata(nodeServicesProvider, electedIndexMetaData);
+ }
+ } catch (Exception e) {
+ logger.warn("recovering index {} failed - recovering as closed", e, electedIndexMetaData.getIndex());
+ electedIndexMetaData = IndexMetaData.builder(electedIndexMetaData).state(IndexMetaData.State.CLOSE).build();
}
+
metaDataBuilder.put(electedIndexMetaData, false);
}
}
diff --git a/core/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java b/core/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java
index 950b4351e1d49..1da82468997ae 100644
--- a/core/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java
+++ b/core/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java
@@ -86,8 +86,8 @@ public GatewayMetaState(Settings settings, NodeEnvironment nodeEnv, MetaStateSer
if (DiscoveryNode.masterNode(settings) || DiscoveryNode.dataNode(settings)) {
try {
ensureNoPre019State();
- pre20Upgrade();
IndexFolderUpgrader.upgradeIndicesIfNeeded(settings, nodeEnv);
+ upgradeMetaData();
long startNS = System.nanoTime();
metaStateService.loadFullState();
logger.debug("took {} to load state", TimeValue.timeValueMillis(TimeValue.nsecToMSec(System.nanoTime() - startNS)));
@@ -222,7 +222,7 @@ private void ensureNoPre019State() throws Exception {
* MetaDataIndexUpgradeService might also update obsolete settings if needed. When this happens we rewrite
* index metadata with new settings.
*/
- private void pre20Upgrade() throws Exception {
+ private void upgradeMetaData() throws Exception {
MetaData metaData = loadMetaState();
List updateIndexMetaData = new ArrayList<>();
for (IndexMetaData indexMetaData : metaData) {
@@ -235,7 +235,7 @@ private void pre20Upgrade() throws Exception {
// means the upgrade can continue. Now it's safe to overwrite index metadata with the new version.
for (IndexMetaData indexMetaData : updateIndexMetaData) {
// since we still haven't upgraded the index folders, we write index state in the old folder
- metaStateService.writeIndex("upgrade", indexMetaData, nodeEnv.resolveIndexFolder(indexMetaData.getIndex().getName()));
+ metaStateService.writeIndex("upgrade", indexMetaData, nodeEnv.resolveIndexFolder(indexMetaData.getIndex().getUUID()));
}
}
diff --git a/core/src/main/java/org/elasticsearch/gateway/GatewayService.java b/core/src/main/java/org/elasticsearch/gateway/GatewayService.java
index 16d67a84c4a30..7dcc45f1c0a9c 100644
--- a/core/src/main/java/org/elasticsearch/gateway/GatewayService.java
+++ b/core/src/main/java/org/elasticsearch/gateway/GatewayService.java
@@ -43,6 +43,8 @@
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
import org.elasticsearch.discovery.Discovery;
import org.elasticsearch.env.NodeEnvironment;
+import org.elasticsearch.index.NodeServicesProvider;
+import org.elasticsearch.indices.IndicesService;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.threadpool.ThreadPool;
@@ -95,9 +97,11 @@ public class GatewayService extends AbstractLifecycleComponent i
@Inject
public GatewayService(Settings settings, AllocationService allocationService, ClusterService clusterService,
ThreadPool threadPool, NodeEnvironment nodeEnvironment, GatewayMetaState metaState,
- TransportNodesListGatewayMetaState listGatewayMetaState, Discovery discovery) {
+ TransportNodesListGatewayMetaState listGatewayMetaState, Discovery discovery,
+ NodeServicesProvider nodeServicesProvider, IndicesService indicesService) {
super(settings);
- this.gateway = new Gateway(settings, clusterService, nodeEnvironment, metaState, listGatewayMetaState, discovery);
+ this.gateway = new Gateway(settings, clusterService, nodeEnvironment, metaState, listGatewayMetaState, discovery,
+ nodeServicesProvider, indicesService);
this.allocationService = allocationService;
this.clusterService = clusterService;
this.threadPool = threadPool;
diff --git a/core/src/main/java/org/elasticsearch/indices/IndicesService.java b/core/src/main/java/org/elasticsearch/indices/IndicesService.java
index b43d33b1bd962..a0dba7089a91b 100644
--- a/core/src/main/java/org/elasticsearch/indices/IndicesService.java
+++ b/core/src/main/java/org/elasticsearch/indices/IndicesService.java
@@ -19,6 +19,7 @@
package org.elasticsearch.indices;
+import com.carrotsearch.hppc.cursors.ObjectCursor;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.util.CollectionUtil;
@@ -33,6 +34,7 @@
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
+import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.breaker.CircuitBreaker;
@@ -66,6 +68,7 @@
import org.elasticsearch.index.fielddata.IndexFieldDataCache;
import org.elasticsearch.index.flush.FlushStats;
import org.elasticsearch.index.get.GetStats;
+import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.merge.MergeStats;
import org.elasticsearch.index.recovery.RecoveryStats;
import org.elasticsearch.index.refresh.RefreshStats;
@@ -74,6 +77,7 @@
import org.elasticsearch.index.shard.IndexEventListener;
import org.elasticsearch.index.shard.IndexShard;
import org.elasticsearch.index.shard.IndexShardState;
+import org.elasticsearch.index.shard.IndexingOperationListener;
import org.elasticsearch.index.shard.IndexingStats;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.index.store.IndexStoreConfig;
@@ -88,9 +92,11 @@
import org.elasticsearch.search.query.QuerySearchResult;
import org.elasticsearch.threadpool.ThreadPool;
+import java.io.Closeable;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
@@ -324,6 +330,7 @@ public IndexService indexServiceSafe(Index index) {
* @throws IndexAlreadyExistsException if the index already exists.
*/
public synchronized IndexService createIndex(final NodeServicesProvider nodeServicesProvider, IndexMetaData indexMetaData, List builtInListeners) throws IOException {
+
if (!lifecycle.started()) {
throw new IllegalStateException("Can't create an index [" + indexMetaData.getIndex() + "], node is closed");
}
@@ -331,37 +338,22 @@ public synchronized IndexService createIndex(final NodeServicesProvider nodeServ
throw new IllegalArgumentException("index must have a real UUID found value: [" + indexMetaData.getIndexUUID() + "]");
}
final Index index = indexMetaData.getIndex();
- final Predicate indexNameMatcher = (indexExpression) -> indexNameExpressionResolver.matchesIndex(index.getName(), indexExpression, clusterService.state());
- final IndexSettings idxSettings = new IndexSettings(indexMetaData, this.settings, indexNameMatcher, indexScopeSetting);
if (hasIndex(index)) {
throw new IndexAlreadyExistsException(index);
}
- logger.debug("creating Index [{}], shards [{}]/[{}{}]",
- indexMetaData.getIndex(),
- idxSettings.getNumberOfShards(),
- idxSettings.getNumberOfReplicas(),
- idxSettings.isShadowReplicaIndex() ? "s" : "");
-
- final IndexModule indexModule = new IndexModule(idxSettings, indexStoreConfig, analysisRegistry);
- pluginsService.onIndexModule(indexModule);
- for (IndexEventListener listener : builtInListeners) {
- indexModule.addIndexEventListener(listener);
- }
+ List finalListeners = new ArrayList<>(builtInListeners);
final IndexEventListener onStoreClose = new IndexEventListener() {
@Override
public void onStoreClosed(ShardId shardId) {
indicesQueryCache.onClose(shardId);
}
};
- indexModule.addIndexEventListener(onStoreClose);
- indexModule.addIndexEventListener(oldShardsStats);
- final IndexEventListener listener = indexModule.freeze();
- listener.beforeIndexCreated(index, idxSettings.getSettings());
- final IndexService indexService = indexModule.newIndexService(nodeEnv, this, nodeServicesProvider, indicesQueryCache, mapperRegistry, indicesFieldDataCache, indexingMemoryController);
+ finalListeners.add(onStoreClose);
+ finalListeners.add(oldShardsStats);
+ final IndexService indexService = createIndexService("create index", nodeServicesProvider, indexMetaData, indicesQueryCache, indicesFieldDataCache, finalListeners, indexingMemoryController);
boolean success = false;
try {
- assert indexService.getIndexEventListener() == listener;
- listener.afterIndexCreated(indexService);
+ indexService.getIndexEventListener().afterIndexCreated(indexService);
indices = newMapBuilder(indices).put(index.getUUID(), indexService).immutableMap();
success = true;
return indexService;
@@ -370,7 +362,54 @@ public void onStoreClosed(ShardId shardId) {
indexService.close("plugins_failed", true);
}
}
+ }
+ /**
+ * This creates a new IndexService without registering it
+ */
+ private synchronized IndexService createIndexService(final String reason, final NodeServicesProvider nodeServicesProvider, IndexMetaData indexMetaData, IndicesQueryCache indicesQueryCache, IndicesFieldDataCache indicesFieldDataCache, List builtInListeners, IndexingOperationListener... indexingOperationListeners) throws IOException {
+ final Index index = indexMetaData.getIndex();
+ final Predicate indexNameMatcher = (indexExpression) -> indexNameExpressionResolver.matchesIndex(index.getName(), indexExpression, clusterService.state());
+ final IndexSettings idxSettings = new IndexSettings(indexMetaData, this.settings, indexNameMatcher, indexScopeSetting);
+ logger.debug("creating Index [{}], shards [{}]/[{}{}] - reason [{}]",
+ indexMetaData.getIndex(),
+ idxSettings.getNumberOfShards(),
+ idxSettings.getNumberOfReplicas(),
+ idxSettings.isShadowReplicaIndex() ? "s" : "", reason);
+
+ final IndexModule indexModule = new IndexModule(idxSettings, indexStoreConfig, analysisRegistry);
+ pluginsService.onIndexModule(indexModule);
+ for (IndexEventListener listener : builtInListeners) {
+ indexModule.addIndexEventListener(listener);
+ }
+ final IndexEventListener listener = indexModule.freeze();
+ listener.beforeIndexCreated(index, idxSettings.getSettings());
+ return indexModule.newIndexService(nodeEnv, this, nodeServicesProvider, indicesQueryCache, mapperRegistry, indicesFieldDataCache, indexingOperationListeners);
+ }
+
+ /**
+ * This method verifies that the given {@link IndexMetaData} holds sane values to create an {@link IndexService}. This method will throw an
+ * exception if the creation fails. The created {@link IndexService} will not be registered and will be closed immediately.
+ */
+ public synchronized void verifyIndexMetadata(final NodeServicesProvider nodeServicesProvider, IndexMetaData metaData) throws IOException {
+ final List closeables = new ArrayList<>();
+ try {
+ IndicesFieldDataCache indicesFieldDataCache = new IndicesFieldDataCache(settings, new IndexFieldDataCache.Listener() {});
+ closeables.add(indicesFieldDataCache);
+ IndicesQueryCache indicesQueryCache = new IndicesQueryCache(settings);
+ closeables.add(indicesQueryCache);
+ // this will also fail if some plugin fails etc. which is nice since we can verify that early
+ final IndexService service = createIndexService("metadata verification", nodeServicesProvider,
+ metaData, indicesQueryCache, indicesFieldDataCache, Collections.emptyList());
+ for (ObjectCursor typeMapping : metaData.getMappings().values()) {
+ // don't apply the default mapping, it has been applied when the mapping was created
+ service.mapperService().merge(typeMapping.value.type(), typeMapping.value.source(),
+ MapperService.MergeReason.MAPPING_RECOVERY, true);
+ }
+ closeables.add(() -> service.close("metadata verification", false));
+ } finally {
+ IOUtils.close(closeables);
+ }
}
/**
diff --git a/core/src/test/java/org/elasticsearch/gateway/GatewayIndexStateIT.java b/core/src/test/java/org/elasticsearch/gateway/GatewayIndexStateIT.java
index d0d9b227948aa..af2894833cb7d 100644
--- a/core/src/test/java/org/elasticsearch/gateway/GatewayIndexStateIT.java
+++ b/core/src/test/java/org/elasticsearch/gateway/GatewayIndexStateIT.java
@@ -19,9 +19,13 @@
package org.elasticsearch.gateway;
+import org.elasticsearch.ElasticsearchException;
+import org.elasticsearch.Version;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse;
import org.elasticsearch.action.get.GetResponse;
+import org.elasticsearch.client.Requests;
+import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.cluster.routing.ShardRoutingState;
@@ -31,7 +35,11 @@
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentFactory;
+import org.elasticsearch.env.NodeEnvironment;
+import org.elasticsearch.index.IndexService;
+import org.elasticsearch.index.mapper.MapperParsingException;
import org.elasticsearch.indices.IndexClosedException;
+import org.elasticsearch.indices.IndicesService;
import org.elasticsearch.node.Node;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
@@ -40,6 +48,7 @@
import static org.elasticsearch.common.settings.Settings.settingsBuilder;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
+import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
import static org.hamcrest.Matchers.equalTo;
@@ -364,4 +373,123 @@ public Settings onNodeStopped(String nodeName) throws Exception {
logger.info("--> verify the doc is there");
assertThat(client().prepareGet("test", "type1", "1").execute().actionGet().isExists(), equalTo(true));
}
+
+ /**
+ * This test really tests worst case scenario where we have a broken setting or any setting that prevents an index from being
+ * allocated in our metadata that we recover. In that case we now have the ability to check the index on local recovery from disk
+ * if it is sane and if we can successfully create an IndexService. This also includes plugins etc.
+ */
+ public void testRecoverBrokenIndexMetadata() throws Exception {
+ logger.info("--> starting one node");
+ internalCluster().startNode();
+ logger.info("--> indexing a simple document");
+ client().prepareIndex("test", "type1", "1").setSource("field1", "value1").setRefresh(true).execute().actionGet();
+ logger.info("--> waiting for green status");
+ if (usually()) {
+ ensureYellow();
+ } else {
+ internalCluster().startNode();
+ client().admin().cluster()
+ .health(Requests.clusterHealthRequest()
+ .waitForGreenStatus()
+ .waitForEvents(Priority.LANGUID)
+ .waitForRelocatingShards(0).waitForNodes("2")).actionGet();
+ }
+ ClusterState state = client().admin().cluster().prepareState().get().getState();
+ IndexMetaData metaData = state.getMetaData().index("test");
+ for (NodeEnvironment services : internalCluster().getInstances(NodeEnvironment.class)) {
+ IndexMetaData brokenMeta = IndexMetaData.builder(metaData).settings(Settings.builder().put(metaData.getSettings())
+ .put(IndexMetaData.SETTING_VERSION_CREATED, Version.V_2_0_0_beta1.id)
+ // this is invalid but should be archived
+ .put("index.similarity.BM25.type", "classic")
+ // this one is not validated ahead of time and breaks allocation
+ .put("index.analysis.filter.myCollator.type", "icu_collation")
+ ).build();
+ IndexMetaData.FORMAT.write(brokenMeta, brokenMeta.getVersion(), services.indexPaths(brokenMeta.getIndex()));
+ }
+ internalCluster().fullRestart();
+ // ensureGreen(closedIndex) waits for the index to show up in the metadata
+ // this is crucial otherwise the state call below might not contain the index yet
+ ensureGreen(metaData.getIndex().getName());
+ state = client().admin().cluster().prepareState().get().getState();
+ assertEquals(IndexMetaData.State.CLOSE, state.getMetaData().index(metaData.getIndex()).getState());
+ assertEquals("classic", state.getMetaData().index(metaData.getIndex()).getSettings().get("archived.index.similarity.BM25.type"));
+ // try to open it with the broken setting - fail again!
+ ElasticsearchException ex = expectThrows(ElasticsearchException.class, () -> client().admin().indices().prepareOpen("test").get());
+ assertEquals(ex.getMessage(), "Failed to verify index " + metaData.getIndex());
+ assertNotNull(ex.getCause());
+ assertEquals(IllegalArgumentException.class, ex.getCause().getClass());
+ assertEquals(ex.getCause().getMessage(), "Unknown tokenfilter type [icu_collation] for [myCollator]");
+
+ client().admin().indices().prepareUpdateSettings()
+ .setSettings(Settings.builder().putNull("index.analysis.filter.myCollator.type")).get();
+ client().admin().indices().prepareOpen("test").get();
+ ensureYellow();
+ logger.info("--> verify 1 doc in the index");
+ assertHitCount(client().prepareSearch().setQuery(matchAllQuery()).get(), 1L);
+ }
+
+ /**
+ * This test really tests worst case scenario where we have a missing analyzer setting.
+ * In that case we now have the ability to check the index on local recovery from disk
+ * if it is sane and if we can successfully create an IndexService.
+ * This also includes plugins etc.
+ */
+ public void testRecoverMissingAnalyzer() throws Exception {
+ logger.info("--> starting one node");
+ internalCluster().startNode();
+ prepareCreate("test").setSettings(Settings.builder()
+ .put("index.analysis.analyzer.test.tokenizer", "keyword")
+ .put("index.number_of_shards", "1"))
+ .addMapping("type1", "{\n" +
+ " \"type1\": {\n" +
+ " \"properties\": {\n" +
+ " \"field1\": {\n" +
+ " \"type\": \"text\",\n" +
+ " \"analyzer\": \"test\"\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ " }}").get();
+ logger.info("--> indexing a simple document");
+ client().prepareIndex("test", "type1", "1").setSource("field1", "value one").setRefresh(true).execute().actionGet();
+ logger.info("--> waiting for green status");
+ if (usually()) {
+ ensureYellow();
+ } else {
+ internalCluster().startNode();
+ client().admin().cluster()
+ .health(Requests.clusterHealthRequest()
+ .waitForGreenStatus()
+ .waitForEvents(Priority.LANGUID)
+ .waitForRelocatingShards(0).waitForNodes("2")).actionGet();
+ }
+ ClusterState state = client().admin().cluster().prepareState().get().getState();
+ IndexMetaData metaData = state.getMetaData().index("test");
+ for (NodeEnvironment services : internalCluster().getInstances(NodeEnvironment.class)) {
+ IndexMetaData brokenMeta = IndexMetaData.builder(metaData).settings(metaData.getSettings()
+ .filter((s) -> "index.analysis.analyzer.test.tokenizer".equals(s) == false)).build();
+ IndexMetaData.FORMAT.write(brokenMeta, brokenMeta.getVersion(), services.indexPaths(brokenMeta.getIndex()));
+ }
+ internalCluster().fullRestart();
+ // ensureGreen(closedIndex) waits for the index to show up in the metadata
+ // this is crucial otherwise the state call below might not contain the index yet
+ ensureGreen(metaData.getIndex().getName());
+ state = client().admin().cluster().prepareState().get().getState();
+ assertEquals(IndexMetaData.State.CLOSE, state.getMetaData().index(metaData.getIndex()).getState());
+
+ // try to open it with the broken setting - fail again!
+ ElasticsearchException ex = expectThrows(ElasticsearchException.class, () -> client().admin().indices().prepareOpen("test").get());
+ assertEquals(ex.getMessage(), "Failed to verify index " + metaData.getIndex());
+ assertNotNull(ex.getCause());
+ assertEquals(MapperParsingException.class, ex.getCause().getClass());
+ assertEquals(ex.getCause().getMessage(), "analyzer [test] not found for field [field1]");
+
+ client().admin().indices().prepareUpdateSettings()
+ .setSettings(Settings.builder().put("index.analysis.analyzer.test.tokenizer", "keyword")).get();
+ client().admin().indices().prepareOpen("test").get();
+ ensureYellow();
+ logger.info("--> verify 1 doc in the index");
+ assertHitCount(client().prepareSearch().setQuery(matchQuery("field1", "value one")).get(), 1L);
+ }
}
diff --git a/core/src/test/java/org/elasticsearch/gateway/GatewayServiceTests.java b/core/src/test/java/org/elasticsearch/gateway/GatewayServiceTests.java
index bf9921a2e23eb..3a3f5f67e776a 100644
--- a/core/src/test/java/org/elasticsearch/gateway/GatewayServiceTests.java
+++ b/core/src/test/java/org/elasticsearch/gateway/GatewayServiceTests.java
@@ -40,8 +40,7 @@ private GatewayService createService(Settings.Builder settings) {
.put("http.enabled", "false")
.put("discovery.type", "local")
.put(settings.build()).build(),
- null, clusterService, null, null, null, null, new NoopDiscovery());
-
+ null, clusterService, null, null, null, null, new NoopDiscovery(), null, null);
}
public void testDefaultRecoverAfterTime() throws IOException {