@Override protected void configure() { IndexShard shard = mock(IndexShard.class); bind(IndexShard.class).toInstance(shard); when(shard.shardId()).thenReturn(shardId); Index index = new Index(TEST_TABLE_NAME); bind(Index.class).toInstance(index); bind(ShardId.class).toInstance(shardId); MapBinder<ReferenceIdent, ShardReferenceImplementation> binder = MapBinder.newMapBinder( binder(), ReferenceIdent.class, ShardReferenceImplementation.class); binder .addBinding(SysShardsTableInfo.INFOS.get(new ColumnIdent("id")).ident()) .toInstance(shardIdExpression); bind(ShardReferenceResolver.class).asEagerSingleton(); bind(AllocationDecider.class).to(DiskThresholdDecider.class); bind(ShardCollectService.class).asEagerSingleton(); bind(DiscoveryService.class).toInstance(discoveryService); // blob stuff MapBinder<ReferenceIdent, BlobShardReferenceImplementation> blobBinder = MapBinder.newMapBinder( binder(), ReferenceIdent.class, BlobShardReferenceImplementation.class); bind(Settings.class).annotatedWith(IndexSettings.class).toInstance(ImmutableSettings.EMPTY); }
private WriteResult<DeleteResponse> shardDeleteOperation( BulkShardRequest request, DeleteRequest deleteRequest, IndexShard indexShard) { Engine.Delete delete = indexShard.prepareDelete( deleteRequest.type(), deleteRequest.id(), deleteRequest.version(), deleteRequest.versionType(), Engine.Operation.Origin.PRIMARY); indexShard.delete(delete); // update the request with the version so it will go to the replicas deleteRequest.versionType(delete.versionType().versionTypeForReplicationAndRecovery()); deleteRequest.version(delete.version()); assert deleteRequest.versionType().validateVersionForWrites(deleteRequest.version()); DeleteResponse deleteResponse = new DeleteResponse( request.index(), deleteRequest.type(), deleteRequest.id(), delete.version(), delete.found()); return new WriteResult(deleteResponse, delete.getTranslogLocation()); }
/** * Restores shard from {@link RestoreSource} associated with this shard in routing table * * @param recoveryState recovery state */ public void restore(final RecoveryState recoveryState) { RestoreSource restoreSource = indexShard.routingEntry().restoreSource(); if (restoreSource == null) { throw new IndexShardRestoreFailedException(shardId, "empty restore source"); } if (logger.isTraceEnabled()) { logger.trace("[{}] restoring shard [{}]", restoreSource.snapshotId(), shardId); } try { recoveryState.getTranslog().totalOperations(0); recoveryState.getTranslog().totalOperationsOnStart(0); indexShard.prepareForIndexRecovery(); IndexShardRepository indexShardRepository = repositoriesService.indexShardRepository(restoreSource.snapshotId().getRepository()); ShardId snapshotShardId = shardId; if (!shardId.getIndex().equals(restoreSource.index())) { snapshotShardId = new ShardId(restoreSource.index(), shardId.id()); } indexShardRepository.restore( restoreSource.snapshotId(), shardId, snapshotShardId, recoveryState); indexShard.prepareForTranslogRecovery(); indexShard.finalizeRecovery(); indexShard.postRecovery("restore done"); restoreService.indexShardRestoreCompleted(restoreSource.snapshotId(), shardId); } catch (Throwable t) { if (Lucene.isCorruptionException(t)) { restoreService.failRestore(restoreSource.snapshotId(), shardId()); } throw new IndexShardRestoreFailedException(shardId, "restore failed", t); } }
private void maybeRefreshEngine() { if (indexSettings.getRefreshInterval().millis() > 0) { for (IndexShard shard : this.shards.values()) { switch (shard.state()) { case CREATED: case RECOVERING: case CLOSED: continue; case POST_RECOVERY: case STARTED: case RELOCATED: try { if (shard.isRefreshNeeded()) { shard.refresh("schedule"); } } catch (EngineClosedException | AlreadyClosedException ex) { // fine - continue; } continue; default: throw new IllegalStateException("unknown state: " + shard.state()); } } } }
/** * 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; }
/** * Execute the given {@link IndexRequest} on a primary shard, throwing a {@link * RetryOnPrimaryException} if the operation needs to be re-tried. */ protected final WriteResult<IndexResponse> executeIndexRequestOnPrimary( BulkShardRequest shardRequest, IndexRequest request, IndexShard indexShard) throws Throwable { Engine.Index operation = prepareIndexOperationOnPrimary(shardRequest, request, indexShard); Mapping update = operation.parsedDoc().dynamicMappingsUpdate(); final ShardId shardId = indexShard.shardId(); if (update != null) { final String indexName = shardId.getIndex(); mappingUpdatedAction.updateMappingOnMasterSynchronously(indexName, request.type(), update); operation = prepareIndexOperationOnPrimary(shardRequest, request, indexShard); update = operation.parsedDoc().dynamicMappingsUpdate(); if (update != null) { throw new RetryOnPrimaryException( shardId, "Dynamics mappings are not available on the node that holds the primary yet"); } } final boolean created = indexShard.index(operation); // update the version on request so it will happen on the replicas final long version = operation.version(); request.version(version); request.versionType(request.versionType().versionTypeForReplicationAndRecovery()); assert request.versionType().validateVersionForWrites(request.version()); return new WriteResult( new IndexResponse( shardId.getIndex(), request.type(), request.id(), request.version(), created), operation.getTranslogLocation()); }
public synchronized void updateMetaData(final IndexMetaData metadata) { if (indexSettings.updateIndexMetaData(metadata)) { final Settings settings = indexSettings.getSettings(); for (final IndexShard shard : this.shards.values()) { try { shard.onRefreshSettings(settings); } catch (Exception e) { logger.warn("[{}] failed to refresh shard settings", e, shard.shardId().id()); } } try { indexStore.onRefreshSettings(settings); } catch (Exception e) { logger.warn("failed to refresh index store settings", e); } try { slowLog.onRefreshSettings( settings); // this will be refactored soon anyway so duplication is ok here } catch (Exception e) { logger.warn("failed to refresh slowlog settings", e); } if (refreshTask.getInterval().equals(indexSettings.getRefreshInterval()) == false) { rescheduleRefreshTasks(); } } }
public PercolateContext( PercolateShardRequest request, SearchShardTarget searchShardTarget, IndexShard indexShard, IndexService indexService, PageCacheRecycler pageCacheRecycler, BigArrays bigArrays, ScriptService scriptService, Query aliasFilter, ParseFieldMatcher parseFieldMatcher) { super(parseFieldMatcher, request); this.indexShard = indexShard; this.indexService = indexService; this.fieldDataService = indexService.fieldData(); this.searchShardTarget = searchShardTarget; this.percolateQueryRegistry = indexShard.percolateRegistry(); this.types = new String[] {request.documentType()}; this.pageCacheRecycler = pageCacheRecycler; this.bigArrays = bigArrays.withCircuitBreaking(); this.querySearchResult = new QuerySearchResult(0, searchShardTarget); this.engineSearcher = indexShard.acquireSearcher("percolate"); this.searcher = new ContextIndexSearcher(this, engineSearcher); this.scriptService = scriptService; this.numberOfShards = request.getNumberOfShards(); this.aliasFilter = aliasFilter; this.startTime = request.getStartTime(); }
/** * Perform phase 3 of the recovery process * * <p>Phase3 again takes a snapshot of the translog, however this time the snapshot is acquired * under a write lock. The translog operations are sent to the target node where they are * replayed. * * <p>{@code InternalEngine#recover} is responsible for taking the snapshot of the translog, and * after phase 3 completes the snapshots from all three phases are released. */ @Override public void phase3(Translog.Snapshot snapshot) throws ElasticsearchException { if (shard.state() == IndexShardState.CLOSED) { throw new IndexShardClosedException(request.shardId()); } cancellableThreads.checkForCancel(); StopWatch stopWatch = new StopWatch().start(); final int totalOperations; logger.trace( "[{}][{}] recovery [phase3] to {}: sending transaction log operations", indexName, shardId, request.targetNode()); // Send the translog operations to the target node totalOperations = sendSnapshot(snapshot); cancellableThreads.execute( new Interruptable() { @Override public void run() throws InterruptedException { // Send the FINALIZE request to the target node. The finalize request // clears unreferenced translog files, refreshes the engine now that // new segments are available, and enables garbage collection of // tombstone files. The shard is also moved to the POST_RECOVERY phase // during this time transportService .submitRequest( request.targetNode(), RecoveryTarget.Actions.FINALIZE, new RecoveryFinalizeRecoveryRequest(request.recoveryId(), request.shardId()), TransportRequestOptions.options() .withTimeout(recoverySettings.internalActionLongTimeout()), EmptyTransportResponseHandler.INSTANCE_SAME) .txGet(); } }); if (request.markAsRelocated()) { // TODO what happens if the recovery process fails afterwards, we need to mark this back to // started try { shard.relocated("to " + request.targetNode()); } catch (IllegalIndexShardStateException e) { // we can ignore this exception since, on the other node, when it moved to phase3 // it will also send shard started, which might cause the index shard we work against // to move be closed by the time we get to the the relocated method } } stopWatch.stop(); logger.trace( "[{}][{}] recovery [phase3] to {}: took [{}]", indexName, shardId, request.targetNode(), stopWatch.totalTime()); response.phase3Time = stopWatch.totalTime().millis(); response.phase3Operations = totalOperations; }
@Override protected EmptyResult shardOperation(OptimizeRequest request, ShardRouting shardRouting) { IndexShard indexShard = indicesService .indexServiceSafe(shardRouting.shardId().getIndex()) .shardSafe(shardRouting.shardId().id()); indexShard.optimize(request); return EmptyResult.INSTANCE; }
@Override public void onCache(ShardId shardId, String fieldName, Accountable ramUsage) { if (shardId != null) { final IndexShard shard = indexService.getShardOrNull(shardId.id()); if (shard != null) { shard.fieldData().onCache(shardId, fieldName, ramUsage); } } }
@Override public void onRemoval(ShardId shardId, String fieldName, boolean wasEvicted, long sizeInBytes) { if (shardId != null) { final IndexShard shard = indexService.getShardOrNull(shardId.id()); if (shard != null) { shard.fieldData().onRemoval(shardId, fieldName, wasEvicted, sizeInBytes); } } }
@Override public void onRemoval(ShardId shardId, Accountable accountable) { if (shardId != null) { final IndexShard shard = indexService.getShardOrNull(shardId.id()); if (shard != null) { long ramBytesUsed = accountable != null ? accountable.ramBytesUsed() : 0L; shard.shardBitsetFilterCache().onRemoval(ramBytesUsed); } } }
@Override public synchronized void close() throws Exception { if (closed == false) { closed = true; for (IndexShard shard : this) { shard.close("eol", false); IOUtils.close(shard.store()); } } else { throw new AlreadyClosedException("too bad"); } }
public synchronized void assertAllEqual(int expectedCount) throws IOException { Set<Uid> primaryIds = getShardDocUIDs(primary); assertThat(primaryIds.size(), equalTo(expectedCount)); for (IndexShard replica : replicas) { Set<Uid> replicaIds = getShardDocUIDs(replica); Set<Uid> temp = new HashSet<>(primaryIds); temp.removeAll(replicaIds); assertThat(replica.routingEntry() + " is missing docs", temp, empty()); temp = new HashSet<>(replicaIds); temp.removeAll(primaryIds); assertThat(replica.routingEntry() + " has extra docs", temp, empty()); } }
public synchronized void removeShard(int shardId, String reason) { final ShardId sId = new ShardId(index(), shardId); final IndexShard indexShard; if (shards.containsKey(shardId) == false) { return; } logger.debug("[{}] closing... (reason: [{}])", shardId, reason); HashMap<Integer, IndexShard> newShards = new HashMap<>(shards); indexShard = newShards.remove(shardId); shards = unmodifiableMap(newShards); closeShard(reason, sId, indexShard, indexShard.store(), indexShard.getIndexEventListener()); logger.debug("[{}] closed (reason: [{}])", shardId, reason); }
// NOTE: O(numShards) cost, but numShards should be smallish? private long getAvgShardSizeInBytes() throws IOException { long sum = 0; int count = 0; for (IndexShard indexShard : this) { sum += indexShard.store().stats().sizeInBytes(); count++; } if (count == 0) { return -1L; } else { return sum / count; } }
/** finalizes the recovery process */ public void finalizeRecovery() { if (shard.state() == IndexShardState.CLOSED) { throw new IndexShardClosedException(request.shardId()); } cancellableThreads.checkForCancel(); StopWatch stopWatch = new StopWatch().start(); logger.trace("[{}][{}] finalizing recovery to {}", indexName, shardId, request.targetNode()); cancellableThreads.execute(recoveryTarget::finalizeRecovery); if (request.isPrimaryRelocation()) { // in case of primary relocation we have to ensure that the cluster state on the primary // relocation target has all // replica shards that have recovered or are still recovering from the current primary, // otherwise replication actions // will not be send to these replicas. To accomplish this, first block new recoveries, then // take version of latest cluster // state. This means that no new recovery can be completed based on information of a newer // cluster state than the current one. try (Releasable ignored = delayNewRecoveries.apply( "primary relocation hand-off in progress or completed for " + shardId)) { final long currentClusterStateVersion = currentClusterStateVersionSupplier.get(); logger.trace( "[{}][{}] waiting on {} to have cluster state with version [{}]", indexName, shardId, request.targetNode(), currentClusterStateVersion); cancellableThreads.execute( () -> recoveryTarget.ensureClusterStateVersion(currentClusterStateVersion)); logger.trace( "[{}][{}] performing relocation hand-off to {}", indexName, shardId, request.targetNode()); cancellableThreads.execute(() -> shard.relocated("to " + request.targetNode())); } /** * if the recovery process fails after setting the shard state to RELOCATED, both relocation * source and target are failed (see {@link IndexShard#updateRoutingEntry}). */ } stopWatch.stop(); logger.trace( "[{}][{}] finalizing recovery to {}: took [{}]", indexName, shardId, request.targetNode(), stopWatch.totalTime()); }
@Override protected ShardValidateQueryResponse shardOperation(ShardValidateQueryRequest request) { IndexService indexService = indicesService.indexServiceSafe(request.shardId().getIndex()); IndexShard indexShard = indexService.getShard(request.shardId().id()); final QueryShardContext queryShardContext = indexService.newQueryShardContext(); queryShardContext.setTypes(request.types()); boolean valid; String explanation = null; String error = null; Engine.Searcher searcher = indexShard.acquireSearcher("validate_query"); DefaultSearchContext searchContext = new DefaultSearchContext( 0, new ShardSearchLocalRequest( request.types(), request.nowInMillis(), request.filteringAliases()), null, searcher, indexService, indexShard, scriptService, pageCacheRecycler, bigArrays, threadPool.estimatedTimeInMillisCounter(), parseFieldMatcher, SearchService.NO_TIMEOUT); SearchContext.setCurrent(searchContext); try { searchContext.parsedQuery(queryShardContext.toQuery(request.query())); searchContext.preProcess(); valid = true; if (request.rewrite()) { explanation = getRewrittenQuery(searcher.searcher(), searchContext.query()); } else if (request.explain()) { explanation = searchContext.filteredQuery().query().toString(); } } catch (QueryShardException | ParsingException e) { valid = false; error = e.getDetailedMessage(); } catch (AssertionError | IOException e) { valid = false; error = e.getMessage(); } finally { searchContext.close(); SearchContext.removeCurrent(); } return new ShardValidateQueryResponse(request.shardId(), valid, explanation, error); }
protected final void processAfter( boolean refresh, IndexShard indexShard, Translog.Location location) { if (refresh) { try { indexShard.refresh("refresh_flag_index"); } catch (Throwable e) { // ignore } } if (indexShard.getTranslogDurability() == Translog.Durabilty.REQUEST && location != null) { indexShard.sync(location); } indexShard.maybeFlush(); }
/** * Creates shard snapshot * * @param snapshotId snapshot id * @param snapshotStatus snapshot status */ public void snapshot(final SnapshotId snapshotId, final IndexShardSnapshotStatus snapshotStatus) { IndexShardRepository indexShardRepository = repositoriesService.indexShardRepository(snapshotId.getRepository()); if (!indexShard.routingEntry().primary()) { throw new IndexShardSnapshotFailedException( shardId, "snapshot should be performed only on primary"); } if (indexShard.routingEntry().relocating()) { // do not snapshot when in the process of relocation of primaries so we won't get conflicts throw new IndexShardSnapshotFailedException(shardId, "cannot snapshot while relocating"); } if (indexShard.state() == IndexShardState.CREATED || indexShard.state() == IndexShardState.RECOVERING) { // shard has just been created, or still recovering throw new IndexShardSnapshotFailedException(shardId, "shard didn't fully recover yet"); } try { SnapshotIndexCommit snapshotIndexCommit = indexShard.snapshotIndex(); try { indexShardRepository.snapshot(snapshotId, shardId, snapshotIndexCommit, snapshotStatus); if (logger.isDebugEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("snapshot (") .append(snapshotId.getSnapshot()) .append(") completed to ") .append(indexShardRepository) .append(", took [") .append(TimeValue.timeValueMillis(snapshotStatus.time())) .append("]\n"); sb.append(" index : version [") .append(snapshotStatus.indexVersion()) .append("], number_of_files [") .append(snapshotStatus.numberOfFiles()) .append("] with total_size [") .append(new ByteSizeValue(snapshotStatus.totalSize())) .append("]\n"); logger.debug(sb.toString()); } } finally { snapshotIndexCommit.close(); } } catch (SnapshotFailedEngineException e) { throw e; } catch (IndexShardSnapshotFailedException e) { throw e; } catch (Throwable e) { throw new IndexShardSnapshotFailedException(shardId, "Failed to snapshot", e); } }
@Override public TerminationHandle warmReader( final IndexShard indexShard, final Engine.Searcher searcher) { final MapperService mapperService = indexShard.mapperService(); final Map<String, MappedFieldType> warmUpGlobalOrdinals = new HashMap<>(); for (DocumentMapper docMapper : mapperService.docMappers(false)) { for (FieldMapper fieldMapper : docMapper.mappers()) { final MappedFieldType fieldType = fieldMapper.fieldType(); final String indexName = fieldType.name(); if (fieldType.eagerGlobalOrdinals() == false) { continue; } warmUpGlobalOrdinals.put(indexName, fieldType); } } final IndexFieldDataService indexFieldDataService = indexShard.indexFieldDataService(); final CountDownLatch latch = new CountDownLatch(warmUpGlobalOrdinals.size()); for (final MappedFieldType fieldType : warmUpGlobalOrdinals.values()) { executor.execute( () -> { try { final long start = System.nanoTime(); IndexFieldData.Global ifd = indexFieldDataService.getForField(fieldType); DirectoryReader reader = searcher.getDirectoryReader(); IndexFieldData<?> global = ifd.loadGlobal(reader); if (reader.leaves().isEmpty() == false) { global.load(reader.leaves().get(0)); } if (indexShard.warmerService().logger().isTraceEnabled()) { indexShard .warmerService() .logger() .trace( "warmed global ordinals for [{}], took [{}]", fieldType.name(), TimeValue.timeValueNanos(System.nanoTime() - start)); } } catch (Exception e) { indexShard .warmerService() .logger() .warn("failed to warm-up global ordinals for [{}]", e, fieldType.name()); } finally { latch.countDown(); } }); } return () -> latch.await(); }
@Override protected ShardUpgradeResult shardOperation(UpgradeRequest request, ShardRouting shardRouting) { IndexShard indexShard = indicesService .indexServiceSafe(shardRouting.shardId().getIndex()) .shardSafe(shardRouting.shardId().id()); org.apache.lucene.util.Version oldestLuceneSegment = indexShard.upgrade(request); // We are using the current version of Elasticsearch as upgrade version since we update mapping // to match the current version return new ShardUpgradeResult( shardRouting.shardId(), indexShard.routingEntry().primary(), Version.CURRENT, oldestLuceneSegment); }
private void maybeFSyncTranslogs() { if (indexSettings.getTranslogDurability() == Translog.Durability.ASYNC) { for (IndexShard shard : this.shards.values()) { try { Translog translog = shard.getTranslog(); if (translog.syncNeeded()) { translog.sync(); } } catch (EngineClosedException | AlreadyClosedException ex) { // fine - continue; } catch (IOException e) { logger.warn("failed to sync translog", e); } } } }
@Override protected void onCancel(String reason, @Nullable Exception suppressedException) { RuntimeException e; if (shard.state() == IndexShardState.CLOSED) { // check if the shard got closed on us e = new IndexShardClosedException( shard.shardId(), "shard is closed and recovery was canceled reason [" + reason + "]"); } else { e = new ExecutionCancelledException("recovery was canceled reason [" + reason + "]"); } if (suppressedException != null) { e.addSuppressed(suppressedException); } throw e; }
private void closeShard( String reason, ShardId sId, IndexShard indexShard, Store store, IndexEventListener listener) { final int shardId = sId.id(); final Settings indexSettings = this.getIndexSettings().getSettings(); try { try { listener.beforeIndexShardClosed(sId, indexShard, indexSettings); } finally { // 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 { // only flush we are we closed (closed index or shutdown) and if we are not deleted final boolean flushEngine = deleted.get() == false && closed.get(); indexShard.close(reason, flushEngine); } catch (Exception e) { logger.debug("[{}] failed to close index shard", e, shardId); // ignore } } // call this before we close the store, so we can release resources for it listener.afterIndexShardClosed(sId, indexShard, indexSettings); } } finally { try { store.close(); } catch (Exception e) { logger.warn( "[{}] failed to close store on shard removal (reason: [{}])", e, shardId, reason); } } }
@Override public int hashCode() { int result = shard.hashCode(); result = 31 * result + (int) (readerVersion ^ (readerVersion >>> 32)); result = 31 * result + value.hashCode(); return result; }
@Override public IndexingResult perform(IndexRequest request) throws Exception { TransportWriteAction.WriteResult<IndexResponse> result = TransportIndexAction.executeIndexRequestOnPrimary(request, primary, null); request.primaryTerm(primary.getPrimaryTerm()); return new IndexingResult(request, result.getResponse()); }
private Set<Uid> getShardDocUIDs(final IndexShard shard) throws IOException { shard.refresh("get_uids"); try (Engine.Searcher searcher = shard.acquireSearcher("test")) { Set<Uid> ids = new HashSet<>(); for (LeafReaderContext leafContext : searcher.reader().leaves()) { LeafReader reader = leafContext.reader(); Bits liveDocs = reader.getLiveDocs(); for (int i = 0; i < reader.maxDoc(); i++) { if (liveDocs == null || liveDocs.get(i)) { Document uuid = reader.document(i, Collections.singleton(UidFieldMapper.NAME)); ids.add(Uid.createUid(uuid.get(UidFieldMapper.NAME))); } } } return ids; } }
@Override public boolean equals(Object o) { if (this == o) return true; CleanupKey that = (CleanupKey) o; if (readerVersion != that.readerVersion) return false; if (!indexShard.equals(that.indexShard)) return false; return true; }