-
Notifications
You must be signed in to change notification settings - Fork 25.2k
Provide a getFromTranslog
method in ShardGetService
#95736
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
8b2b501
1d19928
4cede49
dfe6754
eb532fd
1369f07
5f7d6b6
82d09a2
a009918
8a973f9
4ed865e
b42d930
2b3d730
fe74dfd
41c253f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -143,6 +143,9 @@ public class InternalEngine extends Engine { | |
// we use the hashed variant since we iterate over it and check removal and additions on existing keys | ||
private final LiveVersionMap versionMap; | ||
private final LiveVersionMapArchive liveVersionMapArchive; | ||
// Records the last known generation during which LiveVersionMap was in unsafe mode. This indicates that only after this | ||
// generation it is safe to rely on the LiveVersionMap for a real-time get. | ||
private final AtomicLong lastUnsafeSegmentGenerationForGets = new AtomicLong(-1); | ||
|
||
private volatile SegmentInfos lastCommittedSegmentInfos; | ||
|
||
|
@@ -753,63 +756,95 @@ public GetResult get( | |
DocumentParser documentParser, | ||
Function<Engine.Searcher, Engine.Searcher> searcherWrapper | ||
) { | ||
assert Objects.equals(get.uid().field(), IdFieldMapper.NAME) : get.uid().field(); | ||
assert assertGetUsesIdField(get); | ||
try (ReleasableLock ignored = readLock.acquire()) { | ||
ensureOpen(); | ||
if (get.realtime()) { | ||
final VersionValue versionValue; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All I've done here is to break this down into two pieces. Alternative would be probably a flag to shortcut this, but I'm not sure it makes this more readable. Open for suggestions. |
||
try (Releasable ignore = versionMap.acquireLock(get.uid().bytes())) { | ||
// we need to lock here to access the version map to do this truly in RT | ||
versionValue = getVersionFromMap(get.uid().bytes()); | ||
var result = realtimeGetUnderLock(get, mappingLookup, documentParser, searcherWrapper); | ||
if (result != null) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The normal get, as before, never returns null. |
||
return result; | ||
} | ||
if (versionValue != null) { | ||
if (versionValue.isDelete()) { | ||
return GetResult.NOT_EXISTS; | ||
} | ||
if (get.versionType().isVersionConflictForReads(versionValue.version, get.version())) { | ||
throw new VersionConflictEngineException( | ||
shardId, | ||
"[" + get.id() + "]", | ||
get.versionType().explainConflictForReads(versionValue.version, get.version()) | ||
); | ||
} | ||
if (get.getIfSeqNo() != SequenceNumbers.UNASSIGNED_SEQ_NO | ||
&& (get.getIfSeqNo() != versionValue.seqNo || get.getIfPrimaryTerm() != versionValue.term)) { | ||
throw new VersionConflictEngineException( | ||
shardId, | ||
get.id(), | ||
get.getIfSeqNo(), | ||
get.getIfPrimaryTerm(), | ||
versionValue.seqNo, | ||
versionValue.term | ||
); | ||
} | ||
if (get.isReadFromTranslog()) { | ||
// this is only used for updates - API _GET calls will always read form a reader for consistency | ||
// the update call doesn't need the consistency since it's source only + _parent but parent can go away in 7.0 | ||
pxsalehi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (versionValue.getLocation() != null) { | ||
try { | ||
final Translog.Operation operation = translog.readOperation(versionValue.getLocation()); | ||
if (operation != null) { | ||
return getFromTranslog(get, (Translog.Index) operation, mappingLookup, documentParser, searcherWrapper); | ||
} | ||
} catch (IOException e) { | ||
maybeFailEngine("realtime_get", e); // lets check if the translog has failed with a tragic event | ||
throw new EngineException(shardId, "failed to read operation from translog", e); | ||
} | ||
} else { | ||
trackTranslogLocation.set(true); | ||
return getFromSearcher(get, acquireSearcher("realtime_get", SearcherScope.INTERNAL, searcherWrapper), false); | ||
} else { | ||
// we expose what has been externally expose in a point in time snapshot via an explicit refresh | ||
return getFromSearcher(get, acquireSearcher("get", SearcherScope.EXTERNAL, searcherWrapper), false); | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public GetResult getFromTranslog( | ||
Get get, | ||
MappingLookup mappingLookup, | ||
DocumentParser documentParser, | ||
Function<Searcher, Searcher> searcherWrapper | ||
) { | ||
assert assertGetUsesIdField(get); | ||
try (ReleasableLock ignored = readLock.acquire()) { | ||
ensureOpen(); | ||
return realtimeGetUnderLock(get, mappingLookup, documentParser, searcherWrapper); | ||
} | ||
} | ||
|
||
protected GetResult realtimeGetUnderLock( | ||
Get get, | ||
MappingLookup mappingLookup, | ||
DocumentParser documentParser, | ||
Function<Engine.Searcher, Engine.Searcher> searcherWrapper | ||
) { | ||
assert readLock.isHeldByCurrentThread(); | ||
assert get.realtime(); | ||
final VersionValue versionValue; | ||
pxsalehi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
try (Releasable ignore = versionMap.acquireLock(get.uid().bytes())) { | ||
// we need to lock here to access the version map to do this truly in RT | ||
versionValue = getVersionFromMap(get.uid().bytes()); | ||
} | ||
if (versionValue != null) { | ||
if (versionValue.isDelete()) { | ||
return GetResult.NOT_EXISTS; | ||
} | ||
if (get.versionType().isVersionConflictForReads(versionValue.version, get.version())) { | ||
throw new VersionConflictEngineException( | ||
shardId, | ||
"[" + get.id() + "]", | ||
get.versionType().explainConflictForReads(versionValue.version, get.version()) | ||
); | ||
} | ||
if (get.getIfSeqNo() != SequenceNumbers.UNASSIGNED_SEQ_NO | ||
&& (get.getIfSeqNo() != versionValue.seqNo || get.getIfPrimaryTerm() != versionValue.term)) { | ||
throw new VersionConflictEngineException( | ||
shardId, | ||
get.id(), | ||
get.getIfSeqNo(), | ||
get.getIfPrimaryTerm(), | ||
versionValue.seqNo, | ||
versionValue.term | ||
); | ||
} | ||
if (get.isReadFromTranslog()) { | ||
if (versionValue.getLocation() != null) { | ||
try { | ||
final Translog.Operation operation = translog.readOperation(versionValue.getLocation()); | ||
if (operation != null) { | ||
return getFromTranslog(get, (Translog.Index) operation, mappingLookup, documentParser, searcherWrapper); | ||
} | ||
} catch (IOException e) { | ||
maybeFailEngine("realtime_get", e); // lets check if the translog has failed with a tragic event | ||
throw new EngineException(shardId, "failed to read operation from translog", e); | ||
} | ||
} else { | ||
trackTranslogLocation.set(true); | ||
// We need to start tracking translog locations in the live version map. Refresh and | ||
// serve the get from the internal searcher. | ||
pxsalehi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
assert versionValue.seqNo >= 0 : versionValue; | ||
refreshIfNeeded("realtime_get", versionValue.seqNo); | ||
return getFromSearcher(get, acquireSearcher("realtime_get", SearcherScope.INTERNAL, searcherWrapper), false); | ||
} | ||
return getFromSearcher(get, acquireSearcher("realtime_get", SearcherScope.INTERNAL, searcherWrapper), false); | ||
} else { | ||
// we expose what has been externally expose in a point in time snapshot via an explicit refresh | ||
return getFromSearcher(get, acquireSearcher("get", SearcherScope.EXTERNAL, searcherWrapper), false); | ||
} | ||
assert versionValue.seqNo >= 0 : versionValue; | ||
refreshIfNeeded("realtime_get", versionValue.seqNo); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this refresh needed when the caller is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it is needed since we read from the internal searcher and we need to reload the reader so we can serve the get. The branching is a bit awkward there, I admit. I'll try to improve that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
} | ||
return null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd be fine to return an Optional.empty() here and in upstream methods; this would maybe help to distinguish the case when the doc is not found in the translog vs when it's found but deleted? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional would be also an option! I just wanted to reduce non-essential changes to the Engine interface since that would change the return type in the upstream methods. The null return value is not affecting the normal |
||
} | ||
|
||
/** | ||
|
@@ -915,6 +950,7 @@ private VersionValue getVersionFromMap(BytesRef id) { | |
// but we only need to do this once since the last operation per ID is to add to the version | ||
// map so once we pass this point we can safely lookup from the version map. | ||
if (versionMap.isUnsafe()) { | ||
lastUnsafeSegmentGenerationForGets.set(lastCommittedSegmentInfos.getGeneration() + 1); | ||
refresh("unsafe_version_map", SearcherScope.INTERNAL, true); | ||
} | ||
versionMap.enforceSafeAccess(); | ||
|
@@ -3174,6 +3210,10 @@ protected void waitForCommitDurability(long generation, ActionListener<Void> lis | |
} | ||
} | ||
|
||
public long getLastUnsafeSegmentGenerationForGets() { | ||
return lastUnsafeSegmentGenerationForGets.get(); | ||
} | ||
|
||
protected LiveVersionMapArchive createLiveVersionMapArchive() { | ||
return LiveVersionMapArchive.NOOP_ARCHIVE; | ||
} | ||
|
@@ -3186,4 +3226,9 @@ protected LiveVersionMapArchive getLiveVersionMapArchive() { | |
public LiveVersionMap getLiveVersionMap() { | ||
return versionMap; | ||
} | ||
|
||
private static boolean assertGetUsesIdField(Get get) { | ||
assert Objects.equals(get.uid().field(), IdFieldMapper.NAME) : get.uid().field(); | ||
return true; | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.