|
|
@@ -102,6 +102,7 @@ import java.util.concurrent.locks.Lock;
|
|
|
import java.util.concurrent.locks.ReentrantLock;
|
|
|
import java.util.function.BiFunction;
|
|
|
import java.util.function.LongSupplier;
|
|
|
+import java.util.function.Supplier;
|
|
|
import java.util.stream.Stream;
|
|
|
|
|
|
public class InternalEngine extends Engine {
|
|
|
@@ -189,7 +190,6 @@ public class InternalEngine extends Engine {
|
|
|
translog = openTranslog(engineConfig, translogDeletionPolicy, engineConfig.getGlobalCheckpointSupplier());
|
|
|
assert translog.getGeneration() != null;
|
|
|
this.translog = translog;
|
|
|
- this.localCheckpointTracker = createLocalCheckpointTracker(localCheckpointTrackerSupplier);
|
|
|
this.softDeleteEnabled = engineConfig.getIndexSettings().isSoftDeleteEnabled();
|
|
|
this.softDeletesPolicy = newSoftDeletesPolicy();
|
|
|
this.combinedDeletionPolicy =
|
|
|
@@ -223,6 +223,8 @@ public class InternalEngine extends Engine {
|
|
|
for (ReferenceManager.RefreshListener listener: engineConfig.getInternalRefreshListener()) {
|
|
|
this.internalSearcherManager.addListener(listener);
|
|
|
}
|
|
|
+ this.localCheckpointTracker = createLocalCheckpointTracker(engineConfig, lastCommittedSegmentInfos, logger,
|
|
|
+ () -> acquireSearcher("create_local_checkpoint_tracker", SearcherScope.INTERNAL), localCheckpointTrackerSupplier);
|
|
|
this.lastRefreshedCheckpointListener = new LastRefreshedCheckpointListener(localCheckpointTracker.getCheckpoint());
|
|
|
this.internalSearcherManager.addListener(lastRefreshedCheckpointListener);
|
|
|
success = true;
|
|
|
@@ -238,16 +240,29 @@ public class InternalEngine extends Engine {
|
|
|
logger.trace("created new InternalEngine");
|
|
|
}
|
|
|
|
|
|
- private LocalCheckpointTracker createLocalCheckpointTracker(
|
|
|
- BiFunction<Long, Long, LocalCheckpointTracker> localCheckpointTrackerSupplier) throws IOException {
|
|
|
- final long maxSeqNo;
|
|
|
- final long localCheckpoint;
|
|
|
- final SequenceNumbers.CommitInfo seqNoStats =
|
|
|
- SequenceNumbers.loadSeqNoInfoFromLuceneCommit(store.readLastCommittedSegmentsInfo().userData.entrySet());
|
|
|
- maxSeqNo = seqNoStats.maxSeqNo;
|
|
|
- localCheckpoint = seqNoStats.localCheckpoint;
|
|
|
- logger.trace("recovered maximum sequence number [{}] and local checkpoint [{}]", maxSeqNo, localCheckpoint);
|
|
|
- return localCheckpointTrackerSupplier.apply(maxSeqNo, localCheckpoint);
|
|
|
+ private static LocalCheckpointTracker createLocalCheckpointTracker(EngineConfig engineConfig, SegmentInfos lastCommittedSegmentInfos,
|
|
|
+ Logger logger, Supplier<Searcher> searcherSupplier, BiFunction<Long, Long, LocalCheckpointTracker> localCheckpointTrackerSupplier) {
|
|
|
+ try {
|
|
|
+ final SequenceNumbers.CommitInfo seqNoStats =
|
|
|
+ SequenceNumbers.loadSeqNoInfoFromLuceneCommit(lastCommittedSegmentInfos.userData.entrySet());
|
|
|
+ final long maxSeqNo = seqNoStats.maxSeqNo;
|
|
|
+ final long localCheckpoint = seqNoStats.localCheckpoint;
|
|
|
+ logger.trace("recovered maximum sequence number [{}] and local checkpoint [{}]", maxSeqNo, localCheckpoint);
|
|
|
+ final LocalCheckpointTracker tracker = localCheckpointTrackerSupplier.apply(maxSeqNo, localCheckpoint);
|
|
|
+ // Operations that are optimized using max_seq_no_of_updates optimization must not be processed twice; otherwise, they will
|
|
|
+ // create duplicates in Lucene. To avoid this we check the LocalCheckpointTracker to see if an operation was already processed.
|
|
|
+ // Thus, we need to restore the LocalCheckpointTracker bit by bit to ensure the consistency between LocalCheckpointTracker and
|
|
|
+ // Lucene index. This is not the only solution since we can bootstrap max_seq_no_of_updates with max_seq_no of the commit to
|
|
|
+ // disable the MSU optimization during recovery. Here we prefer to maintain the consistency of LocalCheckpointTracker.
|
|
|
+ if (localCheckpoint < maxSeqNo && engineConfig.getIndexSettings().isSoftDeleteEnabled()) {
|
|
|
+ try (Searcher searcher = searcherSupplier.get()) {
|
|
|
+ Lucene.scanSeqNosInReader(searcher.getDirectoryReader(), localCheckpoint + 1, maxSeqNo, tracker::markSeqNoAsCompleted);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return tracker;
|
|
|
+ } catch (IOException ex) {
|
|
|
+ throw new EngineCreationFailureException(engineConfig.getShardId(), "failed to create local checkpoint tracker", ex);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
private SoftDeletesPolicy newSoftDeletesPolicy() throws IOException {
|
|
|
@@ -665,6 +680,8 @@ public class InternalEngine extends Engine {
|
|
|
} else if (op.seqNo() > docAndSeqNo.seqNo) {
|
|
|
status = OpVsLuceneDocStatus.OP_NEWER;
|
|
|
} else if (op.seqNo() == docAndSeqNo.seqNo) {
|
|
|
+ assert localCheckpointTracker.contains(op.seqNo()) || softDeleteEnabled == false :
|
|
|
+ "local checkpoint tracker is not updated seq_no=" + op.seqNo() + " id=" + op.id();
|
|
|
// load term to tie break
|
|
|
final long existingTerm = VersionsAndSeqNoResolver.loadPrimaryTerm(docAndSeqNo, op.uid().field());
|
|
|
if (op.primaryTerm() > existingTerm) {
|