in accord-core/src/main/java/accord/messages/BeginRecovery.java [253:362]
public boolean visit(Unseekable keyOrRange, TxnId testTxnId, Timestamp testExecuteAt, SummaryStatus status, IsDep dep, Durability minDurability)
{
if (status == NOT_DIRECTLY_WITNESSED || !txnId.witnessedBy(testTxnId))
return true;
int c = testTxnId.compareTo(txnId);
if (c == 0)
return true;
if (c < 0)
{
if (testTxnId.is(ExclusiveSyncPoint) && testTxnId.hlc() > txnId.hlc() && txnId.is(Write))
{
// TODO (required): define our invariants and make sure they're enforce elsewhere.
// Specifically, consider whether truncation/GC can lead to erroneous answers here.
// We're relying on a TxnId that sorts earlier but has a higher HLC to reject a higher
// TxnId with lower HLC.
// Note that right now we are requiring that any sync point is >= any prior syncpoint on
// both HLC and epoch, which likely makes this safe. Let's confirm this works and
// make sure this is properly enforced.
switch (status)
{
default: throw new UnhandledEnum(status);
case APPLIED:
case STABLE:
case COMMITTED:
case ACCEPTED:
if (testExecuteAt.is(HLC_BOUND))
return markSupersedingRejects();
if (status != ACCEPTED)
break;
case PREACCEPTED:
ensureEarlierWait().add(keyOrRange, testTxnId);
case NOTACCEPTED:
case INVALIDATED:
}
return true;
}
switch (dep)
{
default: throw new UnhandledEnum(dep);
case IS_STABLE_DEP:
ensureEarlierNoWait().add(keyOrRange, testTxnId);
break;
case IS_NOT_STABLE_DEP:
/*
* The idea here is to discover those transactions that have been decided to execute after us
* and did not witness us as part of their pre-accept or accept round, as this means that we CANNOT have
* taken the fast path. This is central to safe recovery, as if every transaction that executes later has
* witnessed us we are safe to propose the pre-accept timestamp regardless, whereas if any transaction
* has not witnessed us we can safely invalidate.
*/
return markSupersedingRejects();
case NOT_ELIGIBLE:
switch (status)
{
case INVALIDATED:
// TODO (desired): optionally exclude these and other normally-unnecessary entries on e.g. first recovery attempt
ensureEarlierNoWait().add(keyOrRange, testTxnId);
break;
case ACCEPTED:
if (testExecuteAt.compareTo(txnId) > 0)
ensureEarlierWait().add(keyOrRange, testTxnId);
break;
case PREACCEPTED:
case NOTACCEPTED:
// no need to wait for potential medium path transactions started before us, only after
// however, both privileged coordinator optimisations require waiting for the earlier potential fast path to decide itself
// (that is, if either transaction use the optimisation, we must wait for the earlier transaction)
// TODO (desired): compute against shard whether this is a necessary wait condition - for many quorum configurations it isn't
if (testTxnId.hasPrivilegedCoordinator() || txnId.hasPrivilegedCoordinator())
ensureEarlierWait().add(keyOrRange, testTxnId);
}
}
}
else
{
switch (dep)
{
case IS_NOT_STABLE_DEP:
/*
* The idea here is to discover those transactions that were started after us and have been Accepted
* and did not witness us as part of their pre-accept round, as this means that we CANNOT have taken
* the fast path. This is central to safe recovery, as if every transaction that executes later has
* witnessed us we are safe to propose the pre-accept timestamp regardless, whereas if any transaction
* has not witnessed us we can safely invalidate (us).
*/
return markSupersedingRejects();
case NOT_ELIGIBLE:
// the command doesn't have any coordinator deps; or we are its coordinator and cannot commit on the privileged fast path
case IS_STABLE_DEP:
// the command has been committed with stable deps that witness us, so we're a durable dependency
case IS_COORD_DEP:
// the original coordinator witnessed us, so if it takes the fast or medium path we will be a durable dependency
// if it doesn't, it will take the slow path (and witness us), or be invalidated (in which case it doesn't matter)
break;
case IS_NOT_COORD_DEP:
Invariants.requireArgument(testTxnId.is(PrivilegedCoordinatorWithDeps));
ensureLaterCoordRejects().add(keyOrRange, testTxnId);
}
}
return true;
}