|
@@ -51,7 +51,8 @@ import org.elasticsearch.common.lucene.LoggerInfoStream;
|
|
|
import org.elasticsearch.common.lucene.Lucene;
|
|
|
import org.elasticsearch.common.lucene.index.ElasticsearchDirectoryReader;
|
|
|
import org.elasticsearch.common.lucene.uid.Versions;
|
|
|
-import org.elasticsearch.common.lucene.uid.VersionsResolver;
|
|
|
+import org.elasticsearch.common.lucene.uid.VersionsAndSeqNoResolver;
|
|
|
+import org.elasticsearch.common.lucene.uid.VersionsAndSeqNoResolver.DocIdAndSeqNo;
|
|
|
import org.elasticsearch.common.metrics.CounterMetric;
|
|
|
import org.elasticsearch.common.unit.ByteSizeValue;
|
|
|
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
|
|
@@ -389,10 +390,10 @@ public class InternalEngine extends Engine {
|
|
|
if (versionValue.isDelete()) {
|
|
|
return GetResult.NOT_EXISTS;
|
|
|
}
|
|
|
- if (get.versionType().isVersionConflictForReads(versionValue.getVersion(), get.version())) {
|
|
|
+ if (get.versionType().isVersionConflictForReads(versionValue.version, get.version())) {
|
|
|
Uid uid = Uid.createUid(get.uid().text());
|
|
|
throw new VersionConflictEngineException(shardId, uid.type(), uid.id(),
|
|
|
- get.versionType().explainConflictForReads(versionValue.getVersion(), get.version()));
|
|
|
+ get.versionType().explainConflictForReads(versionValue.version, get.version()));
|
|
|
}
|
|
|
refresh("realtime_get");
|
|
|
}
|
|
@@ -416,6 +417,43 @@ public class InternalEngine extends Engine {
|
|
|
LUCENE_DOC_NOT_FOUND
|
|
|
}
|
|
|
|
|
|
+ private OpVsLuceneDocStatus compareOpToLuceneDocBasedOnSeqNo(final Operation op) throws IOException {
|
|
|
+ assert op.seqNo() != SequenceNumbersService.UNASSIGNED_SEQ_NO : "resolving ops based on seq# but no seqNo is found";
|
|
|
+ final OpVsLuceneDocStatus status;
|
|
|
+ final VersionValue versionValue = versionMap.getUnderLock(op.uid());
|
|
|
+ assert incrementVersionLookup();
|
|
|
+ if (versionValue != null) {
|
|
|
+ if (op.seqNo() > versionValue.seqNo ||
|
|
|
+ (op.seqNo() == versionValue.seqNo && op.primaryTerm() > versionValue.term))
|
|
|
+ status = OpVsLuceneDocStatus.OP_NEWER;
|
|
|
+ else {
|
|
|
+ status = OpVsLuceneDocStatus.OP_STALE_OR_EQUAL;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // load from index
|
|
|
+ assert incrementIndexVersionLookup();
|
|
|
+ try (Searcher searcher = acquireSearcher("load_seq_no")) {
|
|
|
+ DocIdAndSeqNo docAndSeqNo = VersionsAndSeqNoResolver.loadDocIdAndSeqNo(searcher.reader(), op.uid());
|
|
|
+ if (docAndSeqNo == null) {
|
|
|
+ status = OpVsLuceneDocStatus.LUCENE_DOC_NOT_FOUND;
|
|
|
+ } else if (op.seqNo() > docAndSeqNo.seqNo) {
|
|
|
+ status = OpVsLuceneDocStatus.OP_NEWER;
|
|
|
+ } else if (op.seqNo() == docAndSeqNo.seqNo) {
|
|
|
+ // load term to tie break
|
|
|
+ final long existingTerm = VersionsAndSeqNoResolver.loadPrimaryTerm(docAndSeqNo);
|
|
|
+ if (op.primaryTerm() > existingTerm) {
|
|
|
+ status = OpVsLuceneDocStatus.OP_NEWER;
|
|
|
+ } else {
|
|
|
+ status = OpVsLuceneDocStatus.OP_STALE_OR_EQUAL;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ status = OpVsLuceneDocStatus.OP_STALE_OR_EQUAL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return status;
|
|
|
+ }
|
|
|
+
|
|
|
/** resolves the current version of the document, returning null if not found */
|
|
|
private VersionValue resolveDocVersion(final Operation op) throws IOException {
|
|
|
assert incrementVersionLookup(); // used for asserting in tests
|
|
@@ -424,11 +462,10 @@ public class InternalEngine extends Engine {
|
|
|
assert incrementIndexVersionLookup(); // used for asserting in tests
|
|
|
final long currentVersion = loadCurrentVersionFromIndex(op.uid());
|
|
|
if (currentVersion != Versions.NOT_FOUND) {
|
|
|
- versionValue = new VersionValue(currentVersion);
|
|
|
+ versionValue = new VersionValue(currentVersion, SequenceNumbersService.UNASSIGNED_SEQ_NO, 0L);
|
|
|
}
|
|
|
} else if (engineConfig.isEnableGcDeletes() && versionValue.isDelete() &&
|
|
|
- (engineConfig.getThreadPool().relativeTimeInMillis() - versionValue.getTime()) >
|
|
|
- getGcDeletesInMillis()) {
|
|
|
+ (engineConfig.getThreadPool().relativeTimeInMillis() - ((DeleteVersionValue)versionValue).time) > getGcDeletesInMillis()) {
|
|
|
versionValue = null;
|
|
|
}
|
|
|
return versionValue;
|
|
@@ -436,12 +473,13 @@ public class InternalEngine extends Engine {
|
|
|
|
|
|
private OpVsLuceneDocStatus compareOpToLuceneDocBasedOnVersions(final Operation op)
|
|
|
throws IOException {
|
|
|
+ assert op.seqNo() == SequenceNumbersService.UNASSIGNED_SEQ_NO : "op is resolved based on versions but have a seq#";
|
|
|
assert op.version() >= 0 : "versions should be non-negative. got " + op.version();
|
|
|
final VersionValue versionValue = resolveDocVersion(op);
|
|
|
if (versionValue == null) {
|
|
|
return OpVsLuceneDocStatus.LUCENE_DOC_NOT_FOUND;
|
|
|
} else {
|
|
|
- return op.versionType().isVersionConflictForWrites(versionValue.getVersion(), op.version(), versionValue.isDelete()) ?
|
|
|
+ return op.versionType().isVersionConflictForWrites(versionValue.version, op.version(), versionValue.isDelete()) ?
|
|
|
OpVsLuceneDocStatus.OP_STALE_OR_EQUAL : OpVsLuceneDocStatus.OP_NEWER;
|
|
|
}
|
|
|
}
|
|
@@ -601,7 +639,16 @@ public class InternalEngine extends Engine {
|
|
|
// unlike the primary, replicas don't really care to about creation status of documents
|
|
|
// this allows to ignore the case where a document was found in the live version maps in
|
|
|
// a delete state and return false for the created flag in favor of code simplicity
|
|
|
- final OpVsLuceneDocStatus opVsLucene = compareOpToLuceneDocBasedOnVersions(index);
|
|
|
+ final OpVsLuceneDocStatus opVsLucene;
|
|
|
+ if (index.seqNo() != SequenceNumbersService.UNASSIGNED_SEQ_NO) {
|
|
|
+ opVsLucene = compareOpToLuceneDocBasedOnSeqNo(index);
|
|
|
+ } else {
|
|
|
+ // This can happen if the primary is still on an old node and send traffic without seq# or we recover from translog
|
|
|
+ // created by an old version.
|
|
|
+ assert config().getIndexSettings().getIndexVersionCreated().before(Version.V_6_0_0_alpha1_UNRELEASED) :
|
|
|
+ "index is newly created but op has no sequence numbers. op: " + index;
|
|
|
+ opVsLucene = compareOpToLuceneDocBasedOnVersions(index);
|
|
|
+ }
|
|
|
if (opVsLucene == OpVsLuceneDocStatus.OP_STALE_OR_EQUAL) {
|
|
|
plan = IndexingStrategy.processButSkipLucene(false, index.seqNo(), index.version());
|
|
|
} else {
|
|
@@ -633,7 +680,7 @@ public class InternalEngine extends Engine {
|
|
|
currentVersion = Versions.NOT_FOUND;
|
|
|
currentNotFoundOrDeleted = true;
|
|
|
} else {
|
|
|
- currentVersion = versionValue.getVersion();
|
|
|
+ currentVersion = versionValue.version;
|
|
|
currentNotFoundOrDeleted = versionValue.isDelete();
|
|
|
}
|
|
|
if (index.versionType().isVersionConflictForWrites(
|
|
@@ -671,9 +718,9 @@ public class InternalEngine extends Engine {
|
|
|
assert assertDocDoesNotExist(index, canOptimizeAddDocument(index) == false);
|
|
|
index(index.docs(), indexWriter);
|
|
|
}
|
|
|
- versionMap.putUnderLock(index.uid().bytes(), new VersionValue(plan.versionForIndexing));
|
|
|
- return new IndexResult(plan.versionForIndexing, plan.seqNoForIndexing,
|
|
|
- plan.currentNotFoundOrDeleted);
|
|
|
+ versionMap.putUnderLock(index.uid().bytes(),
|
|
|
+ new VersionValue(plan.versionForIndexing, plan.seqNoForIndexing, index.primaryTerm()));
|
|
|
+ return new IndexResult(plan.versionForIndexing, plan.seqNoForIndexing, plan.currentNotFoundOrDeleted);
|
|
|
} catch (Exception ex) {
|
|
|
if (indexWriter.getTragicException() == null) {
|
|
|
/* There is no tragic event recorded so this must be a document failure.
|
|
@@ -873,7 +920,14 @@ public class InternalEngine extends Engine {
|
|
|
// unlike the primary, replicas don't really care to about found status of documents
|
|
|
// this allows to ignore the case where a document was found in the live version maps in
|
|
|
// a delete state and return true for the found flag in favor of code simplicity
|
|
|
- final OpVsLuceneDocStatus opVsLucene = compareOpToLuceneDocBasedOnVersions(delete);
|
|
|
+ final OpVsLuceneDocStatus opVsLucene;
|
|
|
+ if (delete.seqNo() != SequenceNumbersService.UNASSIGNED_SEQ_NO) {
|
|
|
+ opVsLucene = compareOpToLuceneDocBasedOnSeqNo(delete);
|
|
|
+ } else {
|
|
|
+ assert config().getIndexSettings().getIndexVersionCreated().before(Version.V_6_0_0_alpha1_UNRELEASED) :
|
|
|
+ "index is newly created but op has no sequence numbers. op: " + delete;
|
|
|
+ opVsLucene = compareOpToLuceneDocBasedOnVersions(delete);
|
|
|
+ }
|
|
|
|
|
|
final DeletionStrategy plan;
|
|
|
if (opVsLucene == OpVsLuceneDocStatus.OP_STALE_OR_EQUAL) {
|
|
@@ -898,7 +952,7 @@ public class InternalEngine extends Engine {
|
|
|
currentVersion = Versions.NOT_FOUND;
|
|
|
currentlyDeleted = true;
|
|
|
} else {
|
|
|
- currentVersion = versionValue.getVersion();
|
|
|
+ currentVersion = versionValue.version;
|
|
|
currentlyDeleted = versionValue.isDelete();
|
|
|
}
|
|
|
final DeletionStrategy plan;
|
|
@@ -923,7 +977,7 @@ public class InternalEngine extends Engine {
|
|
|
indexWriter.deleteDocuments(delete.uid());
|
|
|
}
|
|
|
versionMap.putUnderLock(delete.uid().bytes(),
|
|
|
- new DeleteVersionValue(plan.versionOfDeletion,
|
|
|
+ new DeleteVersionValue(plan.versionOfDeletion, plan.seqNoOfDeletion, delete.primaryTerm(),
|
|
|
engineConfig.getThreadPool().relativeTimeInMillis()));
|
|
|
return new DeleteResult(
|
|
|
plan.versionOfDeletion, plan.seqNoOfDeletion, plan.currentlyDeleted == false);
|
|
@@ -1235,14 +1289,14 @@ public class InternalEngine extends Engine {
|
|
|
// TODO: not good that we reach into LiveVersionMap here; can we move this inside VersionMap instead? problem is the dirtyLock...
|
|
|
|
|
|
// we only need to prune the deletes map; the current/old version maps are cleared on refresh:
|
|
|
- for (Map.Entry<BytesRef, VersionValue> entry : versionMap.getAllTombstones()) {
|
|
|
+ for (Map.Entry<BytesRef, DeleteVersionValue> entry : versionMap.getAllTombstones()) {
|
|
|
BytesRef uid = entry.getKey();
|
|
|
try (Releasable ignored = acquireLock(uid)) { // can we do it without this lock on each value? maybe batch to a set and get the lock once per set?
|
|
|
|
|
|
// Must re-get it here, vs using entry.getValue(), in case the uid was indexed/deleted since we pulled the iterator:
|
|
|
- VersionValue versionValue = versionMap.getTombstoneUnderLock(uid);
|
|
|
+ DeleteVersionValue versionValue = versionMap.getTombstoneUnderLock(uid);
|
|
|
if (versionValue != null) {
|
|
|
- if (timeMSec - versionValue.getTime() > getGcDeletesInMillis()) {
|
|
|
+ if (timeMSec - versionValue.time > getGcDeletesInMillis()) {
|
|
|
versionMap.removeTombstoneUnderLock(uid);
|
|
|
}
|
|
|
}
|
|
@@ -1490,7 +1544,7 @@ public class InternalEngine extends Engine {
|
|
|
private long loadCurrentVersionFromIndex(Term uid) throws IOException {
|
|
|
assert incrementIndexVersionLookup();
|
|
|
try (Searcher searcher = acquireSearcher("load_version")) {
|
|
|
- return VersionsResolver.loadVersion(searcher.reader(), uid);
|
|
|
+ return VersionsAndSeqNoResolver.loadVersion(searcher.reader(), uid);
|
|
|
}
|
|
|
}
|
|
|
|