useAfterQueue = false;
continue;
}
// Otherwise it's as speculative txn.
// Let's figure out what we need to do with it.
LocalTransaction spec_ts = (LocalTransaction)next;
boolean shouldCommit = false;
long spec_token = spec_ts.getFirstUndoToken(this.partitionId);
if (debug.val)
LOG.debug(String.format("Speculative Txn %s [undoToken=%d, %s]",
spec_ts, spec_token, spec_ts.getSpeculationType()));
// Speculative txns should never be executed without an undo token
assert(spec_token != HStoreConstants.DISABLE_UNDO_LOGGING_TOKEN);
assert(spec_ts.isSpeculative()) : spec_ts + " is not marked as speculative!";
// If the speculative undoToken is null, then this txn didn't execute
// any queries. That means we can always commit it
if (spec_token == HStoreConstants.NULL_UNDO_LOGGING_TOKEN) {
if (debug.val)
LOG.debug(String.format("Speculative Txn %s has a null undoToken at partition %d",
spec_ts, this.partitionId));
toCommit.add(spec_ts);
continue;
}
// Otherwise, look to see if this txn was speculatively executed before the
// first undo token of the distributed txn. That means we know that this guy
// didn't read any modifications made by the dtxn.
if (spec_token < dtxnUndoToken) {
if (debug.val)
LOG.debug(String.format("Speculative Txn %s has an undoToken less than the dtxn %s " +
"at partition %d [%d < %d]",
spec_ts, ts, this.partitionId, spec_token, dtxnUndoToken));
shouldCommit = true;
}
// Ok so at this point we know that our spec txn came *after* the distributed txn
// started. So we need to use our checker to see whether there is a conflict
else if (this.specExecSkipAfter || this.specExecChecker.hasConflictAfter(ts, spec_ts, this.partitionId) == false) {
if (debug.val)
LOG.debug(String.format("Speculative Txn %s does not conflict with dtxn %s at partition %d",
spec_ts, ts, this.partitionId));
shouldCommit = true;
}
if (useAfterQueue == false || shouldCommit == false) {
ClientResponseImpl spec_cr = spec_ts.getClientResponse();
MispredictionException error = new MispredictionException(spec_ts.getTransactionId(),
spec_ts.getTouchedPartitions());
spec_ts.setPendingError(error, false);
spec_cr.setStatus(Status.ABORT_SPECULATIVE);
(useAfterQueue ? toAbortAfter : toAbortBefore).add(spec_ts);
} else {
toCommit.add(spec_ts);
}
} // FOR
// (1) Process all of the aborting txns that need to come *before*
// we abort the dtxn
if (toAbortBefore.isEmpty() == false)
this.processClientResponseBatch(toAbortBefore, Status.ABORT_SPECULATIVE);
// (2) Now abort the dtxn
this.finishTransaction(ts, status);
// (3) Then abort all of the txn that need to come *after* we abort the dtxn
if (toAbortAfter.isEmpty() == false)
this.processClientResponseBatch(toAbortAfter, Status.ABORT_SPECULATIVE);
// (4) Then blast out all of the txns that we want to commit
if (toCommit.isEmpty() == false)
this.processClientResponseBatch(toCommit, Status.OK);
}
// -------------------------------
// DTXN READ-ONLY ABORT or DTXN COMMIT
// -------------------------------
else {
// **IMPORTANT**
// If the dtxn needs to commit, then all we need to do is get the
// last undoToken that we've generated (since we know that it had to
// have been used either by our distributed txn or for one of our
// speculative txns).
//
// If the read-only dtxn needs to abort, then there's nothing we need to
// do, because it didn't make any changes. That means we can just
// commit the last speculatively executed transaction
//
// Once we have this token, we can just make a direct call to the EE
// to commit any changes that came before it. Note that we are using our
// special 'finishWorkEE' method that does not require us to provide
// the transaction that we're committing.
long undoToken = this.lastUndoToken;
if (debug.val)
LOG.debug(String.format("%s - Last undoToken at partition %d => %d",
ts, this.partitionId, undoToken));
// Bombs away!
if (undoToken != this.lastCommittedUndoToken) {
this.finishWorkEE(ts, undoToken, true);
// IMPORTANT: Make sure that we remove the dtxn from the lock queue!
// This is normally done in finishTransaction() but because we're trying
// to be clever and invoke the EE directly, we have to make sure that
// we call it ourselves.
this.queueManager.lockQueueFinished(ts, status, this.partitionId);
}
// Make sure that we mark the dtxn as finished so that we don't
// try to do anything with it later on.
if (hstore_conf.site.exec_readwrite_tracking)
this.markTransactionFinished(ts);
else
ts.markFinished(this.partitionId);
// Now make sure that all of the speculative txns are processed without
// committing (since we just committed any change that they could have made
// up above).
LocalTransaction spec_ts = null;
while ((spec_ts = this.specExecBlocked.pollFirst()) != null) {
ClientResponseImpl spec_cr = spec_ts.getClientResponse();
assert(spec_cr != null);
if (hstore_conf.site.exec_readwrite_tracking)
this.markTransactionFinished(spec_ts);
else
spec_ts.markFinished(this.partitionId);
try {
if (trace.val)
LOG.trace(String.format("%s - Releasing blocked ClientResponse for %s [status=%s]",
ts, spec_ts, spec_cr.getStatus()));