in accord-core/src/main/java/accord/local/Commands.java [134:185]
private static AcceptOutcome preacceptOrRecover(SafeCommandStore safeStore, TxnId txnId, long acceptEpoch, PartialTxn partialTxn, Route<?> route, @Nullable RoutingKey progressKey, Ballot ballot)
{
SafeCommand safeCommand = safeStore.command(txnId);
Command command = safeCommand.current();
int compareBallots = command.promised().compareTo(ballot);
if (compareBallots > 0)
{
logger.trace("{}: skipping preaccept - higher ballot witnessed ({})", txnId, command.promised());
return AcceptOutcome.RejectedBallot;
}
if (command.known().definition.isKnown())
{
Invariants.checkState(command.status() == Invalidated || command.executeAt() != null);
logger.trace("{}: skipping preaccept - already known ({})", txnId, command.status());
// in case of Ballot.ZERO, we must either have a competing recovery coordinator or have late delivery of the
// preaccept; in the former case we should abandon coordination, and in the latter we have already completed
safeCommand.updatePromised(ballot);
return ballot.equals(Ballot.ZERO) ? AcceptOutcome.Redundant : AcceptOutcome.Success;
}
Ranges coordinateRanges = coordinateRanges(safeStore, txnId, acceptEpoch);
Invariants.checkState(!coordinateRanges.isEmpty());
CommonAttributes attrs = updateHomeAndProgressKeys(safeStore, command.txnId(), command, route, progressKey, coordinateRanges);
ProgressShard shard = progressShard(attrs, progressKey, coordinateRanges);
Invariants.checkState(validate(command.status(), attrs, Ranges.EMPTY, coordinateRanges, shard, route, Set, partialTxn, Set, null, Ignore));
// FIXME: this should go into a consumer method
attrs = set(safeStore, command, attrs, Ranges.EMPTY, coordinateRanges, shard, route, partialTxn, Set, null, Ignore);
if (command.executeAt() == null)
{
// unlike in the Accord paper, we partition shards within a node, so that to ensure a total order we must either:
// - use a global logical clock to issue new timestamps; or
// - assign each shard _and_ process a unique id, and use both as components of the timestamp
// if we are performing recovery (i.e. non-zero ballot), do not permit a fast path decision as we want to
// invalidate any transactions that were not completed by their initial coordinator
Timestamp executeAt = ballot.equals(Ballot.ZERO)
? safeStore.commandStore().preaccept(txnId, partialTxn.keys(), safeStore)
: safeStore.time().uniqueNow(txnId);
command = safeCommand.preaccept(attrs, executeAt, ballot);
safeStore.progressLog().preaccepted(command, shard);
}
else
{
// TODO (expected, ?): in the case that we are pre-committed but had not been preaccepted/accepted, should we inform progressLog?
safeCommand.markDefined(attrs, ballot);
}
safeStore.notifyListeners(safeCommand);
return AcceptOutcome.Success;
}