in accord-core/src/main/java/accord/messages/BeginRecovery.java [82:142]
public RecoverReply apply(SafeCommandStore safeStore)
{
SafeCommand safeCommand = safeStore.command(txnId);
switch (Commands.recover(safeStore, txnId, partialTxn, route != null ? route : scope, progressKey, ballot))
{
default:
throw new IllegalStateException("Unhandled Outcome");
case Redundant:
throw new IllegalStateException("Invalid Outcome");
case RejectedBallot:
return new RecoverNack(safeCommand.current().promised());
case Success:
}
Command command = safeCommand.current();
PartialDeps deps = command.partialDeps();
if (!command.known().deps.hasProposedOrDecidedDeps())
{
deps = calculatePartialDeps(safeStore, txnId, partialTxn.keys(), txnId, safeStore.ranges().coordinates(txnId));
}
boolean rejectsFastPath;
Deps earlierCommittedWitness, earlierAcceptedNoWitness;
if (command.hasBeen(PreCommitted))
{
rejectsFastPath = false;
earlierCommittedWitness = earlierAcceptedNoWitness = Deps.NONE;
}
else
{
// TODO (expected): if we can combine these with the earlierAcceptedNoWitness we can avoid persisting deps on Accept
// the problem is we need some way to ensure liveness. If we were to store witnessedAt as a separate register
// we could return these and filter them by whatever the max witnessedAt is that we discover, OR we could
// filter on replicas to exclude any that are started after anything that is committed, since they will have to adopt
// them as a dependency (but we have to make sure we consider dependency rules, so if there's no write and only reads)
// we might still have new transactions block our execution.
Ranges ranges = safeStore.ranges().allAt(txnId);
rejectsFastPath = hasAcceptedStartedAfterWithoutWitnessing(safeStore, txnId, ranges, partialTxn.keys());
if (!rejectsFastPath)
rejectsFastPath = hasCommittedExecutesAfterWithoutWitnessing(safeStore, txnId, ranges, partialTxn.keys());
// TODO (expected, testing): introduce some good unit tests for verifying these two functions in a real repair scenario
// committed txns with an earlier txnid and have our txnid as a dependency
earlierCommittedWitness = committedStartedBeforeAndWitnessed(safeStore, txnId, ranges, partialTxn.keys());
// accepted txns with an earlier txnid that don't have our txnid as a dependency
earlierAcceptedNoWitness = acceptedStartedBeforeWithoutWitnessing(safeStore, txnId, ranges, partialTxn.keys());
}
Status status = command.status();
Ballot accepted = command.accepted();
Timestamp executeAt = command.executeAt();
PartialDeps acceptedDeps = status.phase.compareTo(Phase.Accept) >= 0 ? deps : PartialDeps.NONE;
Writes writes = command.isExecuted() ? command.asExecuted().writes() : null;
Result result = command.isExecuted() ? command.asExecuted().result() : null;
return new RecoverOk(txnId, status, accepted, executeAt, deps, acceptedDeps, earlierCommittedWitness, earlierAcceptedNoWitness, rejectsFastPath, writes, result);
}