private void applyCleanedIndices(final ClusterChangedEvent event) { // handle closed indices, since they are not allocated on a node once they are closed // so applyDeletedIndices might not take them into account for (IndexService indexService : indicesService) { String index = indexService.index().getName(); IndexMetaData indexMetaData = event.state().metaData().index(index); if (indexMetaData != null && indexMetaData.state() == IndexMetaData.State.CLOSE) { for (Integer shardId : indexService.shardIds()) { logger.debug("[{}][{}] removing shard (index is closed)", index, shardId); try { indexService.removeShard(shardId, "removing shard (index is closed)"); } catch (Throwable e) { logger.warn("[{}] failed to remove shard (index is closed)", e, index); } } } } for (IndexService indexService : indicesService) { String index = indexService.index().getName(); if (indexService.shardIds().isEmpty()) { if (logger.isDebugEnabled()) { logger.debug("[{}] cleaning index (no shards allocated)", index); } // clean the index removeIndex(index, "removing index (no shards allocated)"); } } }
/** * Returns the shards to purge, i.e. the local started primary shards that have ttl enabled and * disable_purge to false */ private List<IndexShard> getShardsToPurge() { List<IndexShard> shardsToPurge = new ArrayList<>(); MetaData metaData = clusterService.state().metaData(); for (IndexService indexService : indicesService) { // check the value of disable_purge for this index IndexMetaData indexMetaData = metaData.index(indexService.index()); if (indexMetaData == null) { continue; } if (indexService.getIndexSettings().isTTLPurgeDisabled()) { continue; } // check if ttl is enabled for at least one type of this index boolean hasTTLEnabled = false; for (String type : indexService.mapperService().types()) { DocumentMapper documentType = indexService.mapperService().documentMapper(type); if (documentType.TTLFieldMapper().enabled()) { hasTTLEnabled = true; break; } } if (hasTTLEnabled) { for (IndexShard indexShard : indexService) { if (indexShard.state() == IndexShardState.STARTED && indexShard.routingEntry().primary() && indexShard.routingEntry().started()) { shardsToPurge.add(indexShard); } } } } return shardsToPurge; }
@Override public ClusterState execute(ClusterState currentState) throws Exception { if (cancellableThreads.isCancelled() == false) { // no need to run this if recovery is canceled IndexMetaData indexMetaData = clusterService.state().metaData().getIndices().get(indexService.index().getName()); ImmutableOpenMap<String, MappingMetaData> metaDataMappings = null; if (indexMetaData != null) { metaDataMappings = indexMetaData.getMappings(); } // default mapping should not be sent back, it can only be updated by put mapping API, and // its // a full in place replace, we don't want to override a potential update coming into it for (DocumentMapper documentMapper : indexService.mapperService().docMappers(false)) { MappingMetaData mappingMetaData = metaDataMappings == null ? null : metaDataMappings.get(documentMapper.type()); if (mappingMetaData == null || !documentMapper.refreshSource().equals(mappingMetaData.source())) { // not on master yet in the right form documentMappersToUpdate.add(documentMapper); } } } return currentState; }
@Override public void clusterChanged(final ClusterChangedEvent event) { if (!indicesService.changesAllowed()) { return; } if (!lifecycle.started()) { return; } synchronized (mutex) { // we need to clean the shards and indices we have on this node, since we // are going to recover them again once state persistence is disabled (no master / not // recovered) // TODO: this feels a bit hacky here, a block disables state persistence, and then we clean // the allocated shards, maybe another flag in blocks? if (event.state().blocks().disableStatePersistence()) { for (IndexService indexService : indicesService) { String index = indexService.index().getName(); for (Integer shardId : indexService.shardIds()) { logger.debug("[{}][{}] removing shard (disabled block persistence)", index, shardId); try { indexService.removeShard(shardId, "removing shard (disabled block persistence)"); } catch (Throwable e) { logger.warn("[{}] failed to remove shard (disabled block persistence)", e, index); } } removeIndex(index, "cleaning index (disabled block persistence)"); } return; } cleanFailedShards(event); applyDeletedIndices(event); applyNewIndices(event); applyMappings(event); applyAliases(event); applyNewOrUpdatedShards(event); applyDeletedShards(event); applyCleanedIndices(event); applySettings(event); } }
private void applyDeletedIndices(final ClusterChangedEvent event) { final ClusterState previousState = event.previousState(); final String localNodeId = event.state().nodes().localNodeId(); assert localNodeId != null; for (IndexService indexService : indicesService) { IndexMetaData indexMetaData = event.state().metaData().index(indexService.index().name()); if (indexMetaData != null) { if (!indexMetaData.isSameUUID(indexService.indexUUID())) { logger.debug( "[{}] mismatch on index UUIDs between cluster state and local state, cleaning the index so it will be recreated", indexMetaData.index()); deleteIndex( indexMetaData.index(), "mismatch on index UUIDs between cluster state and local state, cleaning the index so it will be recreated"); } } } for (String index : event.indicesDeleted()) { if (logger.isDebugEnabled()) { logger.debug("[{}] cleaning index, no longer part of the metadata", index); } final Settings indexSettings; final IndexService idxService = indicesService.indexService(index); if (idxService != null) { indexSettings = idxService.getIndexSettings(); deleteIndex(index, "index no longer part of the metadata"); } else { final IndexMetaData metaData = previousState.metaData().index(index); assert metaData != null; indexSettings = metaData.settings(); indicesService.deleteClosedIndex( "closed index no longer part of the metadata", metaData, event.state()); } try { nodeIndexDeletedAction.nodeIndexDeleted(event.state(), index, indexSettings, localNodeId); } catch (Throwable e) { logger.debug("failed to send to master index {} deleted event", e, index); } } }
private void applyDeletedShards(final ClusterChangedEvent event) { RoutingNodes.RoutingNodeIterator routingNode = event.state().readOnlyRoutingNodes().routingNodeIter(event.state().nodes().localNodeId()); if (routingNode == null) { return; } IntHashSet newShardIds = new IntHashSet(); for (IndexService indexService : indicesService) { String index = indexService.index().name(); IndexMetaData indexMetaData = event.state().metaData().index(index); if (indexMetaData == null) { continue; } // now, go over and delete shards that needs to get deleted newShardIds.clear(); for (ShardRouting shard : routingNode) { if (shard.index().equals(index)) { newShardIds.add(shard.id()); } } for (Integer existingShardId : indexService.shardIds()) { if (!newShardIds.contains(existingShardId)) { if (indexMetaData.state() == IndexMetaData.State.CLOSE) { if (logger.isDebugEnabled()) { logger.debug("[{}][{}] removing shard (index is closed)", index, existingShardId); } indexService.removeShard(existingShardId, "removing shard (index is closed)"); } else { // we can just remove the shard, without cleaning it locally, since we will clean it // when all shards are allocated in the IndicesStore if (logger.isDebugEnabled()) { logger.debug("[{}][{}] removing shard (not allocated)", index, existingShardId); } indexService.removeShard(existingShardId, "removing shard (not allocated)"); } } } } }
public void testResetRootDocId() throws Exception { Directory directory = newDirectory(); IndexWriterConfig iwc = new IndexWriterConfig(null); iwc.setMergePolicy(NoMergePolicy.INSTANCE); RandomIndexWriter indexWriter = new RandomIndexWriter(random(), directory, iwc); List<Document> documents = new ArrayList<>(); // 1 segment with, 1 root document, with 3 nested sub docs Document document = new Document(); document.add( new Field(UidFieldMapper.NAME, "type#1", UidFieldMapper.Defaults.NESTED_FIELD_TYPE)); document.add( new Field(TypeFieldMapper.NAME, "__nested_field", TypeFieldMapper.Defaults.FIELD_TYPE)); documents.add(document); document = new Document(); document.add( new Field(UidFieldMapper.NAME, "type#1", UidFieldMapper.Defaults.NESTED_FIELD_TYPE)); document.add( new Field(TypeFieldMapper.NAME, "__nested_field", TypeFieldMapper.Defaults.FIELD_TYPE)); documents.add(document); document = new Document(); document.add( new Field(UidFieldMapper.NAME, "type#1", UidFieldMapper.Defaults.NESTED_FIELD_TYPE)); document.add( new Field(TypeFieldMapper.NAME, "__nested_field", TypeFieldMapper.Defaults.FIELD_TYPE)); documents.add(document); document = new Document(); document.add(new Field(UidFieldMapper.NAME, "type#1", UidFieldMapper.Defaults.FIELD_TYPE)); document.add(new Field(TypeFieldMapper.NAME, "test", TypeFieldMapper.Defaults.FIELD_TYPE)); documents.add(document); indexWriter.addDocuments(documents); indexWriter.commit(); documents.clear(); // 1 segment with: // 1 document, with 1 nested subdoc document = new Document(); document.add( new Field(UidFieldMapper.NAME, "type#2", UidFieldMapper.Defaults.NESTED_FIELD_TYPE)); document.add( new Field(TypeFieldMapper.NAME, "__nested_field", TypeFieldMapper.Defaults.FIELD_TYPE)); documents.add(document); document = new Document(); document.add(new Field(UidFieldMapper.NAME, "type#2", UidFieldMapper.Defaults.FIELD_TYPE)); document.add(new Field(TypeFieldMapper.NAME, "test", TypeFieldMapper.Defaults.FIELD_TYPE)); documents.add(document); indexWriter.addDocuments(documents); documents.clear(); // and 1 document, with 1 nested subdoc document = new Document(); document.add( new Field(UidFieldMapper.NAME, "type#3", UidFieldMapper.Defaults.NESTED_FIELD_TYPE)); document.add( new Field(TypeFieldMapper.NAME, "__nested_field", TypeFieldMapper.Defaults.FIELD_TYPE)); documents.add(document); document = new Document(); document.add(new Field(UidFieldMapper.NAME, "type#3", UidFieldMapper.Defaults.FIELD_TYPE)); document.add(new Field(TypeFieldMapper.NAME, "test", TypeFieldMapper.Defaults.FIELD_TYPE)); documents.add(document); indexWriter.addDocuments(documents); indexWriter.commit(); indexWriter.close(); IndexService indexService = createIndex("test"); DirectoryReader directoryReader = DirectoryReader.open(directory); directoryReader = ElasticsearchDirectoryReader.wrap(directoryReader, new ShardId(indexService.index(), 0)); IndexSearcher searcher = new IndexSearcher(directoryReader); indexService .mapperService() .merge( "test", new CompressedXContent( PutMappingRequest.buildFromSimplifiedDef("test", "nested_field", "type=nested") .string()), MapperService.MergeReason.MAPPING_UPDATE, false); SearchContext searchContext = createSearchContext(indexService); AggregationContext context = new AggregationContext(searchContext); AggregatorFactories.Builder builder = AggregatorFactories.builder(); NestedAggregatorBuilder factory = new NestedAggregatorBuilder("test", "nested_field"); builder.addAggregator(factory); AggregatorFactories factories = builder.build(context, null); searchContext.aggregations(new SearchContextAggregations(factories)); Aggregator[] aggs = factories.createTopLevelAggregators(); BucketCollector collector = BucketCollector.wrap(Arrays.asList(aggs)); collector.preCollection(); // A regular search always exclude nested docs, so we use NonNestedDocsFilter.INSTANCE here // (otherwise MatchAllDocsQuery would be sufficient) // We exclude root doc with uid type#2, this will trigger the bug if we don't reset the root doc // when we process a new segment, because // root doc type#3 and root doc type#1 have the same segment docid BooleanQuery.Builder bq = new BooleanQuery.Builder(); bq.add(Queries.newNonNestedFilter(), Occur.MUST); bq.add(new TermQuery(new Term(UidFieldMapper.NAME, "type#2")), Occur.MUST_NOT); searcher.search(new ConstantScoreQuery(bq.build()), collector); collector.postCollection(); Nested nested = (Nested) aggs[0].buildAggregation(0); // The bug manifests if 6 docs are returned, because currentRootDoc isn't reset the previous // child docs from the first segment are emitted as hits. assertThat(nested.getDocCount(), equalTo(4L)); directoryReader.close(); directory.close(); }
/** * Ensures that the mapping in the cluster state is the same as the mapping in our mapper service. * If the mapping is not in sync, sends a request to update it in the cluster state and blocks * until it has finished being updated. */ private void updateMappingOnMaster() { // we test that the cluster state is in sync with our in memory mapping stored by the // mapperService // we have to do it under the "cluster state update" thread to make sure that one doesn't modify // it // while we're checking final BlockingQueue<DocumentMapper> documentMappersToUpdate = ConcurrentCollections.newBlockingQueue(); final CountDownLatch latch = new CountDownLatch(1); final AtomicReference<Throwable> mappingCheckException = new AtomicReference<>(); // we use immediate as this is a very light weight check and we don't wait to delay recovery clusterService.submitStateUpdateTask( "recovery_mapping_check", Priority.IMMEDIATE, new MappingUpdateTask( clusterService, indexService, recoverySettings, latch, documentMappersToUpdate, mappingCheckException, this.cancellableThreads)); cancellableThreads.execute( new Interruptable() { @Override public void run() throws InterruptedException { latch.await(); } }); if (mappingCheckException.get() != null) { logger.warn("error during mapping check, failing recovery", mappingCheckException.get()); throw new ElasticsearchException("error during mapping check", mappingCheckException.get()); } if (documentMappersToUpdate.isEmpty()) { return; } final CountDownLatch updatedOnMaster = new CountDownLatch(documentMappersToUpdate.size()); MappingUpdatedAction.MappingUpdateListener listener = new MappingUpdatedAction.MappingUpdateListener() { @Override public void onMappingUpdate() { updatedOnMaster.countDown(); } @Override public void onFailure(Throwable t) { logger.debug( "{} recovery to {}: failed to update mapping on master", request.shardId(), request.targetNode(), t); updatedOnMaster.countDown(); } }; for (DocumentMapper documentMapper : documentMappersToUpdate) { mappingUpdatedAction.updateMappingOnMaster( indexService.index().getName(), documentMapper, indexService.indexUUID(), listener); } cancellableThreads.execute( new Interruptable() { @Override public void run() throws InterruptedException { try { if (!updatedOnMaster.await( recoverySettings.internalActionTimeout().millis(), TimeUnit.MILLISECONDS)) { logger.debug( "[{}][{}] recovery [phase2] to {}: waiting on pending mapping update timed out. waited [{}]", indexName, shardId, request.targetNode(), recoverySettings.internalActionTimeout()); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); logger.debug("interrupted while waiting for mapping to update on master"); } } }); }