Uncommitted mapping updates should not efect existing indices (#21306)
When processing a mapping updates, the master current creates an `IndexService` and uses its mapper service to do the hard work. However, if the master is also a data node and it already has an instance of `IndexService`, we currently reuse the the `MapperService` of that instance. Sadly, since mapping updates are change the in memory objects, this means that a mapping change that can rejected later on during cluster state publishing will leave a side effect on the index in question, bypassing the cluster state safety mechanism. This commit removes this optimization and replaces the `IndexService` creation with a direct creation of a `MapperService`. Also, this fixes an issue multiple from multiple shards for the same field caused unneeded cluster state publishing as the current code always created a new cluster state. This were discovered while researching #21189
This commit is contained in:
parent
ad94bea0bb
commit
6d9af2fff4
|
@ -32,7 +32,7 @@ public class PutMappingClusterStateUpdateRequest extends IndicesClusterStateUpda
|
|||
|
||||
private boolean updateAllTypes = false;
|
||||
|
||||
PutMappingClusterStateUpdateRequest() {
|
||||
public PutMappingClusterStateUpdateRequest() {
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -20,9 +20,9 @@
|
|||
package org.elasticsearch.cluster.metadata;
|
||||
|
||||
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
||||
|
||||
import org.apache.logging.log4j.message.ParameterizedMessage;
|
||||
import org.apache.logging.log4j.util.Supplier;
|
||||
import org.apache.lucene.util.IOUtils;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingClusterStateUpdateRequest;
|
||||
import org.elasticsearch.cluster.AckedClusterStateTaskListener;
|
||||
|
@ -34,7 +34,6 @@ import org.elasticsearch.cluster.node.DiscoveryNode;
|
|||
import org.elasticsearch.cluster.service.ClusterService;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.Priority;
|
||||
import org.elasticsearch.common.collect.Tuple;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.compress.CompressedXContent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
|
@ -51,10 +50,8 @@ import java.io.IOException;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
/**
|
||||
* Service responsible for submitting mapping changes
|
||||
*/
|
||||
|
@ -215,27 +212,24 @@ public class MetaDataMappingService extends AbstractComponent {
|
|||
@Override
|
||||
public BatchResult<PutMappingClusterStateUpdateRequest> execute(ClusterState currentState,
|
||||
List<PutMappingClusterStateUpdateRequest> tasks) throws Exception {
|
||||
Set<Index> indicesToClose = new HashSet<>();
|
||||
Map<Index, MapperService> indexMapperServices = new HashMap<>();
|
||||
BatchResult.Builder<PutMappingClusterStateUpdateRequest> builder = BatchResult.builder();
|
||||
try {
|
||||
// precreate incoming indices;
|
||||
for (PutMappingClusterStateUpdateRequest request : tasks) {
|
||||
try {
|
||||
for (Index index : request.indices()) {
|
||||
final IndexMetaData indexMetaData = currentState.metaData().getIndexSafe(index);
|
||||
if (indicesService.hasIndex(indexMetaData.getIndex()) == false) {
|
||||
// if the index does not exists we create it once, add all types to the mapper service and
|
||||
// close it later once we are done with mapping update
|
||||
indicesToClose.add(indexMetaData.getIndex());
|
||||
IndexService indexService = indicesService.createIndex(indexMetaData, Collections.emptyList());
|
||||
if (indexMapperServices.containsKey(indexMetaData.getIndex()) == false) {
|
||||
MapperService mapperService = indicesService.createIndexMapperService(indexMetaData);
|
||||
indexMapperServices.put(index, mapperService);
|
||||
// add mappings for all types, we need them for cross-type validation
|
||||
for (ObjectCursor<MappingMetaData> mapping : indexMetaData.getMappings().values()) {
|
||||
indexService.mapperService().merge(mapping.value.type(), mapping.value.source(),
|
||||
mapperService.merge(mapping.value.type(), mapping.value.source(),
|
||||
MapperService.MergeReason.MAPPING_RECOVERY, request.updateAllTypes());
|
||||
}
|
||||
}
|
||||
}
|
||||
currentState = applyRequest(currentState, request);
|
||||
currentState = applyRequest(currentState, request, indexMapperServices);
|
||||
builder.success(request);
|
||||
} catch (Exception e) {
|
||||
builder.failure(request, e);
|
||||
|
@ -243,34 +237,33 @@ public class MetaDataMappingService extends AbstractComponent {
|
|||
}
|
||||
return builder.build(currentState);
|
||||
} finally {
|
||||
for (Index index : indicesToClose) {
|
||||
indicesService.removeIndex(index, "created for mapping processing");
|
||||
}
|
||||
IOUtils.close(indexMapperServices.values());
|
||||
}
|
||||
}
|
||||
|
||||
private ClusterState applyRequest(ClusterState currentState, PutMappingClusterStateUpdateRequest request) throws IOException {
|
||||
private ClusterState applyRequest(ClusterState currentState, PutMappingClusterStateUpdateRequest request,
|
||||
Map<Index, MapperService> indexMapperServices) throws IOException {
|
||||
String mappingType = request.type();
|
||||
CompressedXContent mappingUpdateSource = new CompressedXContent(request.source());
|
||||
final MetaData metaData = currentState.metaData();
|
||||
final List<Tuple<IndexService, IndexMetaData>> updateList = new ArrayList<>();
|
||||
final List<IndexMetaData> updateList = new ArrayList<>();
|
||||
for (Index index : request.indices()) {
|
||||
IndexService indexService = indicesService.indexServiceSafe(index);
|
||||
MapperService mapperService = indexMapperServices.get(index);
|
||||
// IMPORTANT: always get the metadata from the state since it get's batched
|
||||
// and if we pull it from the indexService we might miss an update etc.
|
||||
final IndexMetaData indexMetaData = currentState.getMetaData().getIndexSafe(index);
|
||||
|
||||
// this is paranoia... just to be sure we use the exact same indexService and metadata tuple on the update that
|
||||
// this is paranoia... just to be sure we use the exact same metadata tuple on the update that
|
||||
// we used for the validation, it makes this mechanism little less scary (a little)
|
||||
updateList.add(new Tuple<>(indexService, indexMetaData));
|
||||
updateList.add(indexMetaData);
|
||||
// try and parse it (no need to add it here) so we can bail early in case of parsing exception
|
||||
DocumentMapper newMapper;
|
||||
DocumentMapper existingMapper = indexService.mapperService().documentMapper(request.type());
|
||||
DocumentMapper existingMapper = mapperService.documentMapper(request.type());
|
||||
if (MapperService.DEFAULT_MAPPING.equals(request.type())) {
|
||||
// _default_ types do not go through merging, but we do test the new settings. Also don't apply the old default
|
||||
newMapper = indexService.mapperService().parse(request.type(), mappingUpdateSource, false);
|
||||
newMapper = mapperService.parse(request.type(), mappingUpdateSource, false);
|
||||
} else {
|
||||
newMapper = indexService.mapperService().parse(request.type(), mappingUpdateSource, existingMapper == null);
|
||||
newMapper = mapperService.parse(request.type(), mappingUpdateSource, existingMapper == null);
|
||||
if (existingMapper != null) {
|
||||
// first, simulate: just call merge and ignore the result
|
||||
existingMapper.merge(newMapper.mapping(), request.updateAllTypes());
|
||||
|
@ -286,9 +279,9 @@ public class MetaDataMappingService extends AbstractComponent {
|
|||
for (ObjectCursor<MappingMetaData> mapping : indexMetaData.getMappings().values()) {
|
||||
String parentType = newMapper.parentFieldMapper().type();
|
||||
if (parentType.equals(mapping.value.type()) &&
|
||||
indexService.mapperService().getParentTypes().contains(parentType) == false) {
|
||||
mapperService.getParentTypes().contains(parentType) == false) {
|
||||
throw new IllegalArgumentException("can't add a _parent field that points to an " +
|
||||
"already existing type, that isn't already a parent");
|
||||
"already existing type, that isn't already a parent");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -306,24 +299,25 @@ public class MetaDataMappingService extends AbstractComponent {
|
|||
throw new InvalidTypeNameException("Document mapping type name can't start with '_', found: [" + mappingType + "]");
|
||||
}
|
||||
MetaData.Builder builder = MetaData.builder(metaData);
|
||||
for (Tuple<IndexService, IndexMetaData> toUpdate : updateList) {
|
||||
boolean updated = false;
|
||||
for (IndexMetaData indexMetaData : updateList) {
|
||||
// do the actual merge here on the master, and update the mapping source
|
||||
// we use the exact same indexService and metadata we used to validate above here to actually apply the update
|
||||
final IndexService indexService = toUpdate.v1();
|
||||
final IndexMetaData indexMetaData = toUpdate.v2();
|
||||
final Index index = indexMetaData.getIndex();
|
||||
final MapperService mapperService = indexMapperServices.get(index);
|
||||
CompressedXContent existingSource = null;
|
||||
DocumentMapper existingMapper = indexService.mapperService().documentMapper(mappingType);
|
||||
DocumentMapper existingMapper = mapperService.documentMapper(mappingType);
|
||||
if (existingMapper != null) {
|
||||
existingSource = existingMapper.mappingSource();
|
||||
}
|
||||
DocumentMapper mergedMapper = indexService.mapperService().merge(mappingType, mappingUpdateSource, MapperService.MergeReason.MAPPING_UPDATE, request.updateAllTypes());
|
||||
DocumentMapper mergedMapper = mapperService.merge(mappingType, mappingUpdateSource, MapperService.MergeReason.MAPPING_UPDATE, request.updateAllTypes());
|
||||
CompressedXContent updatedSource = mergedMapper.mappingSource();
|
||||
|
||||
if (existingSource != null) {
|
||||
if (existingSource.equals(updatedSource)) {
|
||||
// same source, no changes, ignore it
|
||||
} else {
|
||||
updated = true;
|
||||
// use the merged mapping source
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("{} update_mapping [{}] with source [{}]", index, mergedMapper.type(), updatedSource);
|
||||
|
@ -333,6 +327,7 @@ public class MetaDataMappingService extends AbstractComponent {
|
|||
|
||||
}
|
||||
} else {
|
||||
updated = true;
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("{} create_mapping [{}] with source [{}]", index, mappingType, updatedSource);
|
||||
} else if (logger.isInfoEnabled()) {
|
||||
|
@ -343,13 +338,16 @@ public class MetaDataMappingService extends AbstractComponent {
|
|||
IndexMetaData.Builder indexMetaDataBuilder = IndexMetaData.builder(indexMetaData);
|
||||
// Mapping updates on a single type may have side-effects on other types so we need to
|
||||
// update mapping metadata on all types
|
||||
for (DocumentMapper mapper : indexService.mapperService().docMappers(true)) {
|
||||
for (DocumentMapper mapper : mapperService.docMappers(true)) {
|
||||
indexMetaDataBuilder.putMapping(new MappingMetaData(mapper.mappingSource()));
|
||||
}
|
||||
builder.put(indexMetaDataBuilder);
|
||||
}
|
||||
|
||||
return ClusterState.builder(currentState).metaData(builder).build();
|
||||
if (updated) {
|
||||
return ClusterState.builder(currentState).metaData(builder).build();
|
||||
} else {
|
||||
return currentState;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.elasticsearch.index.cache.query.DisabledQueryCache;
|
|||
import org.elasticsearch.index.cache.query.IndexQueryCache;
|
||||
import org.elasticsearch.index.cache.query.QueryCache;
|
||||
import org.elasticsearch.index.engine.EngineFactory;
|
||||
import org.elasticsearch.index.mapper.MapperService;
|
||||
import org.elasticsearch.index.shard.IndexEventListener;
|
||||
import org.elasticsearch.index.shard.IndexSearcherWrapper;
|
||||
import org.elasticsearch.index.shard.IndexingOperationListener;
|
||||
|
@ -359,6 +360,16 @@ public final class IndexModule {
|
|||
searchOperationListeners, indexOperationListeners);
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a new mapper service to do administrative work like mapping updates. This *should not* be used for document parsing.
|
||||
* doing so will result in an exception.
|
||||
*/
|
||||
public MapperService newIndexMapperService(MapperRegistry mapperRegistry) throws IOException {
|
||||
return new MapperService(indexSettings, analysisRegistry.build(indexSettings),
|
||||
new SimilarityService(indexSettings, similarities), mapperRegistry,
|
||||
() -> { throw new UnsupportedOperationException("no index query shard context available"); });
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces a certain query cache to use instead of the default one. If this is set
|
||||
* and query caching is not disabled with {@code index.queries.cache.enabled}, then
|
||||
|
|
|
@ -93,7 +93,6 @@ import static org.elasticsearch.common.collect.MapBuilder.newMapBuilder;
|
|||
public class IndexService extends AbstractIndexComponent implements IndicesClusterStateService.AllocatedIndex<IndexShard> {
|
||||
|
||||
private final IndexEventListener eventListener;
|
||||
private final IndexAnalyzers indexAnalyzers;
|
||||
private final IndexFieldDataService indexFieldData;
|
||||
private final BitsetFilterCache bitsetFilterCache;
|
||||
private final NodeEnvironment nodeEnv;
|
||||
|
@ -142,12 +141,11 @@ public class IndexService extends AbstractIndexComponent implements IndicesClust
|
|||
List<IndexingOperationListener> indexingOperationListeners) throws IOException {
|
||||
super(indexSettings);
|
||||
this.indexSettings = indexSettings;
|
||||
this.indexAnalyzers = registry.build(indexSettings);
|
||||
this.similarityService = similarityService;
|
||||
this.mapperService = new MapperService(indexSettings, indexAnalyzers, similarityService, mapperRegistry,
|
||||
this.mapperService = new MapperService(indexSettings, registry.build(indexSettings), similarityService, mapperRegistry,
|
||||
// we parse all percolator queries as they would be parsed on shard 0
|
||||
() -> newQueryShardContext(0, null, () -> {
|
||||
throw new IllegalArgumentException("Percolator queries are not allowed to use the curent timestamp");
|
||||
throw new IllegalArgumentException("Percolator queries are not allowed to use the current timestamp");
|
||||
}));
|
||||
this.indexFieldData = new IndexFieldDataService(indexSettings, indicesFieldDataCache, circuitBreakerService, mapperService);
|
||||
this.shardStoreDeleter = shardStoreDeleter;
|
||||
|
@ -225,7 +223,7 @@ public class IndexService extends AbstractIndexComponent implements IndicesClust
|
|||
}
|
||||
|
||||
public IndexAnalyzers getIndexAnalyzers() {
|
||||
return this.indexAnalyzers;
|
||||
return this.mapperService.getIndexAnalyzers();
|
||||
}
|
||||
|
||||
public MapperService mapperService() {
|
||||
|
@ -249,7 +247,7 @@ public class IndexService extends AbstractIndexComponent implements IndicesClust
|
|||
}
|
||||
}
|
||||
} finally {
|
||||
IOUtils.close(bitsetFilterCache, indexCache, indexFieldData, indexAnalyzers, refreshTask, fsyncTask);
|
||||
IOUtils.close(bitsetFilterCache, indexCache, indexFieldData, mapperService, refreshTask, fsyncTask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ import org.elasticsearch.indices.InvalidTypeNameException;
|
|||
import org.elasticsearch.indices.TypeMissingException;
|
||||
import org.elasticsearch.indices.mapper.MapperRegistry;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
@ -62,7 +63,7 @@ import static java.util.Collections.emptySet;
|
|||
import static java.util.Collections.unmodifiableMap;
|
||||
import static org.elasticsearch.common.collect.MapBuilder.newMapBuilder;
|
||||
|
||||
public class MapperService extends AbstractIndexComponent {
|
||||
public class MapperService extends AbstractIndexComponent implements Closeable {
|
||||
|
||||
/**
|
||||
* The reason why a mapping is being merged.
|
||||
|
@ -624,6 +625,11 @@ public class MapperService extends AbstractIndexComponent {
|
|||
return parentTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
indexAnalyzers.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether a field is a metadata field.
|
||||
*/
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
package org.elasticsearch.indices;
|
||||
|
||||
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.message.ParameterizedMessage;
|
||||
import org.apache.lucene.index.DirectoryReader;
|
||||
|
@ -430,6 +429,21 @@ public class IndicesService extends AbstractLifecycleComponent
|
|||
indicesQueriesRegistry, clusterService, client, indicesQueryCache, mapperRegistry, indicesFieldDataCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a new mapper service for the given index, in order to do administrative work like mapping updates.
|
||||
* This *should not* be used for document parsing. Doing so will result in an exception.
|
||||
*
|
||||
* Note: the returned {@link MapperService} should be closed when unneeded.
|
||||
*/
|
||||
public synchronized MapperService createIndexMapperService(IndexMetaData indexMetaData) throws IOException {
|
||||
final Index index = indexMetaData.getIndex();
|
||||
final Predicate<String> indexNameMatcher = (indexExpression) -> indexNameExpressionResolver.matchesIndex(index.getName(), indexExpression, clusterService.state());
|
||||
final IndexSettings idxSettings = new IndexSettings(indexMetaData, this.settings, indexNameMatcher, indexScopeSetting);
|
||||
final IndexModule indexModule = new IndexModule(idxSettings, indexStoreConfig, analysisRegistry);
|
||||
pluginsService.onIndexModule(indexModule);
|
||||
return indexModule.newIndexMapperService(mapperRegistry);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method verifies that the given {@code metaData} holds sane values to create an {@link IndexService}.
|
||||
* This method tries to update the meta data of the created {@link IndexService} if the given {@code metaDataUpdate} is different from the given {@code metaData}.
|
||||
|
|
|
@ -18,11 +18,17 @@
|
|||
*/
|
||||
package org.elasticsearch.cluster.metadata;
|
||||
|
||||
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingClusterStateUpdateRequest;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.service.ClusterService;
|
||||
import org.elasticsearch.common.compress.CompressedXContent;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.index.IndexService;
|
||||
import org.elasticsearch.index.mapper.DocumentMapper;
|
||||
import org.elasticsearch.test.ESSingleNodeTestCase;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
|
@ -63,4 +69,34 @@ public class MetaDataMappingServiceTests extends ESSingleNodeTestCase {
|
|||
assertThat(documentMapper.parentFieldMapper().active(), is(true));
|
||||
}
|
||||
|
||||
public void testMappingClusterStateUpdateDoesntChangeExistingIndices() throws Exception {
|
||||
final IndexService indexService = createIndex("test", client().admin().indices().prepareCreate("test").addMapping("type"));
|
||||
final CompressedXContent currentMapping = indexService.mapperService().documentMapper("type").mappingSource();
|
||||
|
||||
final MetaDataMappingService mappingService = getInstanceFromNode(MetaDataMappingService.class);
|
||||
final ClusterService clusterService = getInstanceFromNode(ClusterService.class);
|
||||
// TODO - it will be nice to get a random mapping generator
|
||||
final PutMappingClusterStateUpdateRequest request = new PutMappingClusterStateUpdateRequest().type("type");
|
||||
request.source("{ \"properties\" { \"field\": { \"type\": \"string\" }}}");
|
||||
mappingService.putMappingExecutor.execute(clusterService.state(), Collections.singletonList(request));
|
||||
assertThat(indexService.mapperService().documentMapper("type").mappingSource(), equalTo(currentMapping));
|
||||
}
|
||||
|
||||
public void testClusterStateIsNotChangedWithIdenticalMappings() throws Exception {
|
||||
createIndex("test", client().admin().indices().prepareCreate("test").addMapping("type"));
|
||||
|
||||
final MetaDataMappingService mappingService = getInstanceFromNode(MetaDataMappingService.class);
|
||||
final ClusterService clusterService = getInstanceFromNode(ClusterService.class);
|
||||
final PutMappingClusterStateUpdateRequest request = new PutMappingClusterStateUpdateRequest().type("type");
|
||||
request.source("{ \"properties\" { \"field\": { \"type\": \"string\" }}}");
|
||||
ClusterState result = mappingService.putMappingExecutor.execute(clusterService.state(), Collections.singletonList(request))
|
||||
.resultingState;
|
||||
|
||||
assertFalse(result != clusterService.state());
|
||||
|
||||
ClusterState result2 = mappingService.putMappingExecutor.execute(result, Collections.singletonList(request))
|
||||
.resultingState;
|
||||
|
||||
assertSame(result, result2);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,16 +35,27 @@ import org.elasticsearch.gateway.GatewayMetaState;
|
|||
import org.elasticsearch.gateway.LocalAllocateDangledIndices;
|
||||
import org.elasticsearch.gateway.MetaStateService;
|
||||
import org.elasticsearch.index.Index;
|
||||
import org.elasticsearch.index.IndexModule;
|
||||
import org.elasticsearch.index.IndexService;
|
||||
import org.elasticsearch.index.IndexSettings;
|
||||
import org.elasticsearch.index.mapper.Mapper;
|
||||
import org.elasticsearch.index.mapper.MapperService;
|
||||
import org.elasticsearch.index.mapper.StringFieldMapper;
|
||||
import org.elasticsearch.index.shard.ShardId;
|
||||
import org.elasticsearch.index.shard.ShardPath;
|
||||
import org.elasticsearch.index.similarity.BM25SimilarityProvider;
|
||||
import org.elasticsearch.indices.IndicesService.ShardDeletionCheckResult;
|
||||
import org.elasticsearch.plugins.MapperPlugin;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.test.ESSingleNodeTestCase;
|
||||
import org.elasticsearch.test.IndexSettingsModule;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
@ -53,6 +64,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcke
|
|||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
|
||||
public class IndicesServiceTests extends ESSingleNodeTestCase {
|
||||
|
@ -65,6 +77,30 @@ public class IndicesServiceTests extends ESSingleNodeTestCase {
|
|||
return getInstanceFromNode(NodeEnvironment.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Class<? extends Plugin>> getPlugins() {
|
||||
ArrayList<Class<? extends Plugin>> plugins = new ArrayList<>(super.getPlugins());
|
||||
plugins.add(TestPlugin.class);
|
||||
return plugins;
|
||||
}
|
||||
|
||||
public static class TestPlugin extends Plugin implements MapperPlugin {
|
||||
|
||||
public TestPlugin() {}
|
||||
|
||||
@Override
|
||||
public Map<String, Mapper.TypeParser> getMappers() {
|
||||
return Collections.singletonMap("fake-mapper", new StringFieldMapper.TypeParser());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIndexModule(IndexModule indexModule) {
|
||||
super.onIndexModule(indexModule);
|
||||
indexModule.addSimilarity("fake-similarity", BM25SimilarityProvider::new);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean resetNodeAfterTest() {
|
||||
return true;
|
||||
|
@ -328,4 +364,26 @@ public class IndicesServiceTests extends ESSingleNodeTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that teh {@link MapperService} created by {@link IndicesService#createIndexMapperService(IndexMetaData)} contains
|
||||
* custom types and similarities registered by plugins
|
||||
*/
|
||||
public void testStandAloneMapperServiceWithPlugins() throws IOException {
|
||||
final String indexName = "test";
|
||||
final Index index = new Index(indexName, UUIDs.randomBase64UUID());
|
||||
final IndicesService indicesService = getIndicesService();
|
||||
final Settings idxSettings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT)
|
||||
.put(IndexMetaData.SETTING_INDEX_UUID, index.getUUID())
|
||||
.put(IndexModule.SIMILARITY_SETTINGS_PREFIX + ".test.type", "fake-similarity")
|
||||
.build();
|
||||
final IndexMetaData indexMetaData = new IndexMetaData.Builder(index.getName())
|
||||
.settings(idxSettings)
|
||||
.numberOfShards(1)
|
||||
.numberOfReplicas(0)
|
||||
.build();
|
||||
MapperService mapperService = indicesService.createIndexMapperService(indexMetaData);
|
||||
assertNotNull(mapperService.documentMapperParser().parserContext("type").typeParser("fake-mapper"));
|
||||
assertThat(mapperService.documentMapperParser().parserContext("type").getSimilarity("test"),
|
||||
instanceOf(BM25SimilarityProvider.class));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ import org.elasticsearch.common.collect.ImmutableOpenMap;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.discovery.DiscoverySettings;
|
||||
import org.elasticsearch.discovery.zen.ElectMasterService;
|
||||
import org.elasticsearch.gateway.GatewayAllocator;
|
||||
import org.elasticsearch.index.Index;
|
||||
import org.elasticsearch.index.IndexService;
|
||||
|
@ -324,7 +325,12 @@ public class RareClusterStateIT extends ESIntegTestCase {
|
|||
// Here we want to test that everything goes well if the mappings that
|
||||
// are needed for a document are not available on the replica at the
|
||||
// time of indexing it
|
||||
final List<String> nodeNames = internalCluster().startNodesAsync(2).get();
|
||||
final List<String> nodeNames = internalCluster().startNodesAsync(2,
|
||||
Settings.builder()
|
||||
.put(ElectMasterService.DISCOVERY_ZEN_MINIMUM_MASTER_NODES_SETTING.getKey(), 2)
|
||||
.put(DiscoverySettings.COMMIT_TIMEOUT_SETTING.getKey(), "30s") // explicitly set so it won't default to publish timeout
|
||||
.put(DiscoverySettings.PUBLISH_TIMEOUT_SETTING.getKey(), "0s") // don't wait post commit as we are blocking things by design
|
||||
.build()).get();
|
||||
assertFalse(client().admin().cluster().prepareHealth().setWaitForNodes("2").get().isTimedOut());
|
||||
|
||||
final String master = internalCluster().getMasterName();
|
||||
|
|
Loading…
Reference in New Issue