/* * Licensed to ElasticSearch and Shay Banon under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. ElasticSearch licenses this * file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.elasticsearch.index.service; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.UnmodifiableIterator; import org.elasticsearch.ElasticSearchException; import org.elasticsearch.ElasticSearchIllegalStateException; import org.elasticsearch.ElasticSearchInterruptedException; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.inject.*; import org.elasticsearch.common.io.FileSystemUtils; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.gateway.none.NoneGateway; import org.elasticsearch.index.*; import org.elasticsearch.index.aliases.IndexAliasesService; import org.elasticsearch.index.analysis.AnalysisService; import org.elasticsearch.index.cache.IndexCache; import org.elasticsearch.index.deletionpolicy.DeletionPolicyModule; import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.engine.EngineModule; import org.elasticsearch.index.engine.IndexEngine; import org.elasticsearch.index.gateway.IndexGateway; import org.elasticsearch.index.gateway.IndexShardGatewayModule; import org.elasticsearch.index.gateway.IndexShardGatewayService; import org.elasticsearch.index.get.ShardGetModule; import org.elasticsearch.index.indexing.ShardIndexingModule; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.merge.policy.MergePolicyModule; import org.elasticsearch.index.merge.policy.MergePolicyProvider; import org.elasticsearch.index.merge.scheduler.MergeSchedulerModule; import org.elasticsearch.index.percolator.PercolatorService; import org.elasticsearch.index.query.IndexQueryParserService; import org.elasticsearch.index.search.stats.ShardSearchModule; import org.elasticsearch.index.settings.IndexSettings; import org.elasticsearch.index.settings.IndexSettingsService; import org.elasticsearch.index.shard.IndexShardCreationException; import org.elasticsearch.index.shard.IndexShardManagement; import org.elasticsearch.index.shard.IndexShardModule; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.shard.service.IndexShard; import org.elasticsearch.index.shard.service.InternalIndexShard; import org.elasticsearch.index.similarity.SimilarityService; import org.elasticsearch.index.store.IndexStore; import org.elasticsearch.index.store.Store; import org.elasticsearch.index.store.StoreModule; import org.elasticsearch.index.translog.Translog; import org.elasticsearch.index.translog.TranslogModule; import org.elasticsearch.index.translog.TranslogService; import org.elasticsearch.indices.IndicesLifecycle; import org.elasticsearch.indices.InternalIndicesLifecycle; import org.elasticsearch.plugins.PluginsService; import org.elasticsearch.plugins.ShardsPluginsModule; import org.elasticsearch.threadpool.ThreadPool; import java.io.IOException; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import static com.google.common.collect.Maps.newHashMap; import static org.elasticsearch.common.collect.MapBuilder.newMapBuilder; /** * */ public class InternalIndexService extends AbstractIndexComponent implements IndexService { private final Injector injector; private final Settings indexSettings; private final NodeEnvironment nodeEnv; private final ThreadPool threadPool; private final PluginsService pluginsService; private final InternalIndicesLifecycle indicesLifecycle; private final PercolatorService percolatorService; private final AnalysisService analysisService; private final MapperService mapperService; private final IndexQueryParserService queryParserService; private final SimilarityService similarityService; private final IndexAliasesService aliasesService; private final IndexCache indexCache; private final IndexEngine indexEngine; private final IndexGateway indexGateway; private final IndexStore indexStore; private final IndexSettingsService settingsService; private volatile ImmutableMap<Integer, Injector> shardsInjectors = ImmutableMap.of(); private volatile ImmutableMap<Integer, IndexShard> shards = ImmutableMap.of(); private volatile boolean closed = false; @Inject public InternalIndexService(Injector injector, Index index, @IndexSettings Settings indexSettings, NodeEnvironment nodeEnv, ThreadPool threadPool, PercolatorService percolatorService, AnalysisService analysisService, MapperService mapperService, IndexQueryParserService queryParserService, SimilarityService similarityService, IndexAliasesService aliasesService, IndexCache indexCache, IndexEngine indexEngine, IndexGateway indexGateway, IndexStore indexStore, IndexSettingsService settingsService) { super(index, indexSettings); this.injector = injector; this.nodeEnv = nodeEnv; this.threadPool = threadPool; this.indexSettings = indexSettings; this.percolatorService = percolatorService; this.analysisService = analysisService; this.mapperService = mapperService; this.queryParserService = queryParserService; this.similarityService = similarityService; this.aliasesService = aliasesService; this.indexCache = indexCache; this.indexEngine = indexEngine; this.indexGateway = indexGateway; this.indexStore = indexStore; this.settingsService = settingsService; this.pluginsService = injector.getInstance(PluginsService.class); this.indicesLifecycle = (InternalIndicesLifecycle) injector.getInstance(IndicesLifecycle.class); } @Override public int numberOfShards() { return shards.size(); } @Override public UnmodifiableIterator<IndexShard> iterator() { return shards.values().iterator(); } @Override public boolean hasShard(int shardId) { return shards.containsKey(shardId); } @Override public IndexShard shard(int shardId) { return shards.get(shardId); } @Override public IndexShard shardSafe(int shardId) throws IndexShardMissingException { IndexShard indexShard = shard(shardId); if (indexShard == null) { throw new IndexShardMissingException(new ShardId(index, shardId)); } return indexShard; } @Override public ImmutableSet<Integer> shardIds() { return ImmutableSet.copyOf(shards.keySet()); } @Override public Injector injector() { return injector; } @Override public IndexGateway gateway() { return indexGateway; } @Override public IndexSettingsService settingsService() { return this.settingsService; } @Override public IndexStore store() { return indexStore; } @Override public IndexCache cache() { return indexCache; } @Override public PercolatorService percolateService() { return this.percolatorService; } @Override public AnalysisService analysisService() { return this.analysisService; } @Override public MapperService mapperService() { return mapperService; } @Override public IndexQueryParserService queryParserService() { return queryParserService; } @Override public SimilarityService similarityService() { return similarityService; } @Override public IndexAliasesService aliasesService() { return aliasesService; } @Override public IndexEngine engine() { return indexEngine; } public void close(final boolean delete, final String reason, @Nullable Executor executor) { synchronized (this) { closed = true; } Set<Integer> shardIds = shardIds(); final CountDownLatch latch = new CountDownLatch(shardIds.size()); for (final int shardId : shardIds) { executor = executor == null ? threadPool.generic() : executor; executor.execute(new Runnable() { @Override public void run() { try { deleteShard(shardId, delete, !delete, delete, reason); } catch (Exception e) { logger.warn("failed to close shard, delete [{}]", e, delete); } finally { latch.countDown(); } } }); } try { latch.await(); } catch (InterruptedException e) { throw new ElasticSearchInterruptedException("interrupted closing index [ " + index().name() + "]", e); } } @Override public Injector shardInjector(int shardId) throws ElasticSearchException { return shardsInjectors.get(shardId); } @Override public Injector shardInjectorSafe(int shardId) throws IndexShardMissingException { Injector shardInjector = shardInjector(shardId); if (shardInjector == null) { throw new IndexShardMissingException(new ShardId(index, shardId)); } return shardInjector; } @Override public synchronized IndexShard createShard(int sShardId) throws ElasticSearchException { if (closed) { throw new ElasticSearchIllegalStateException("Can't create shard [" + index.name() + "][" + sShardId + "], closed"); } ShardId shardId = new ShardId(index, sShardId); if (shardsInjectors.containsKey(shardId.id())) { throw new IndexShardAlreadyExistsException(shardId + " already exists"); } indicesLifecycle.beforeIndexShardCreated(shardId); logger.debug("creating shard_id [{}]", shardId.id()); ModulesBuilder modules = new ModulesBuilder(); modules.add(new ShardsPluginsModule(indexSettings, pluginsService)); modules.add(new IndexShardModule(indexSettings, shardId)); modules.add(new ShardIndexingModule()); modules.add(new ShardSearchModule()); modules.add(new ShardGetModule()); modules.add(new StoreModule(indexSettings, injector.getInstance(IndexStore.class))); modules.add(new DeletionPolicyModule(indexSettings)); modules.add(new MergePolicyModule(indexSettings)); modules.add(new MergeSchedulerModule(indexSettings)); modules.add(new TranslogModule(indexSettings)); modules.add(new EngineModule(indexSettings)); modules.add(new IndexShardGatewayModule(injector.getInstance(IndexGateway.class))); Injector shardInjector; try { shardInjector = modules.createChildInjector(injector); } catch (CreationException e) { throw new IndexShardCreationException(shardId, Injectors.getFirstErrorFailure(e)); } catch (Throwable e) { throw new IndexShardCreationException(shardId, e); } shardsInjectors = newMapBuilder(shardsInjectors).put(shardId.id(), shardInjector).immutableMap(); IndexShard indexShard = shardInjector.getInstance(IndexShard.class); indicesLifecycle.afterIndexShardCreated(indexShard); shards = newMapBuilder(shards).put(shardId.id(), indexShard).immutableMap(); return indexShard; } @Override public synchronized void cleanShard(int shardId, String reason) throws ElasticSearchException { deleteShard(shardId, true, false, false, reason); } @Override public synchronized void removeShard(int shardId, String reason) throws ElasticSearchException { deleteShard(shardId, false, false, false, reason); } private void deleteShard(int shardId, boolean delete, boolean snapshotGateway, boolean deleteGateway, String reason) throws ElasticSearchException { Injector shardInjector; IndexShard indexShard; synchronized (this) { Map<Integer, Injector> tmpShardInjectors = newHashMap(shardsInjectors); shardInjector = tmpShardInjectors.remove(shardId); if (shardInjector == null) { if (!delete) { return; } throw new IndexShardMissingException(new ShardId(index, shardId)); } shardsInjectors = ImmutableMap.copyOf(tmpShardInjectors); if (delete) { logger.debug("deleting shard_id [{}]", shardId); } Map<Integer, IndexShard> tmpShardsMap = newHashMap(shards); indexShard = tmpShardsMap.remove(shardId); shards = ImmutableMap.copyOf(tmpShardsMap); } ShardId sId = new ShardId(index, shardId); indicesLifecycle.beforeIndexShardClosed(sId, indexShard, delete); for (Class<? extends CloseableIndexComponent> closeable : pluginsService.shardServices()) { try { shardInjector.getInstance(closeable).close(delete); } catch (Exception e) { logger.debug("failed to clean plugin shard service [{}]", e, closeable); } } try { // now we can close the translog service, we need to close it before the we close the shard shardInjector.getInstance(TranslogService.class).close(); } catch (Exception e) { logger.debug("failed to close translog service", e); // ignore } // close shard actions if (indexShard != null) { shardInjector.getInstance(IndexShardManagement.class).close(); } // this logic is tricky, we want to close the engine so we rollback the changes done to it // and close the shard so no operations are allowed to it if (indexShard != null) { try { ((InternalIndexShard) indexShard).close(reason); } catch (Exception e) { logger.debug("failed to close index shard", e); // ignore } } try { shardInjector.getInstance(Engine.class).close(); } catch (Exception e) { logger.debug("failed to close engine", e); // ignore } try { shardInjector.getInstance(MergePolicyProvider.class).close(delete); } catch (Exception e) { logger.debug("failed to close merge policy provider", e); // ignore } try { // now, we can snapshot to the gateway, it will be only the translog if (snapshotGateway) { shardInjector.getInstance(IndexShardGatewayService.class).snapshotOnClose(); } } catch (Exception e) { logger.debug("failed to snapshot gateway on close", e); // ignore } try { shardInjector.getInstance(IndexShardGatewayService.class).close(deleteGateway); } catch (Exception e) { logger.debug("failed to close index shard gateway", e); // ignore } try { // now we can close the translog shardInjector.getInstance(Translog.class).close(delete); } catch (Exception e) { logger.debug("failed to close translog", e); // ignore } // call this before we close the store, so we can release resources for it indicesLifecycle.afterIndexShardClosed(sId, delete); // if we delete or have no gateway or the store is not persistent, clean the store... Store store = shardInjector.getInstance(Store.class); if (delete || indexGateway.type().equals(NoneGateway.TYPE) || !indexStore.persistent()) { try { store.fullDelete(); } catch (IOException e) { logger.warn("failed to clean store on shard deletion", e); } } // and close it try { store.close(); } catch (IOException e) { logger.warn("failed to close store on shard deletion", e); } Injectors.close(injector); // delete the shard location if needed if (delete || indexGateway.type().equals(NoneGateway.TYPE)) { FileSystemUtils.deleteRecursively(nodeEnv.shardLocations(sId)); } } }