LOG.trace(String.format("Current Transaction at partition #%d\n%s",
this.partitionId, ts.debug()));
}
if (hstore_conf.site.txn_counters) TransactionCounter.EXECUTED.inc(ts.getProcedure());
ClientResponseImpl cresponse = null;
VoltProcedure previous = this.currentVoltProc;
try {
this.currentVoltProc = volt_proc;
ts.markControlCodeExecuted();
cresponse = volt_proc.call(ts, ts.getProcedureParameters().toArray()); // Blocking...
// VoltProcedure.call() should handle any exceptions thrown by the transaction
// If we get anything out here then that's bad news
} catch (Throwable ex) {
if (this.isShuttingDown() == false) {
SQLStmt last[] = volt_proc.voltLastQueriesExecuted();
LOG.fatal("Unexpected error while executing " + ts, ex);
if (last.length > 0) {
LOG.fatal(String.format("Last Queries Executed [%d]: %s",
last.length, Arrays.toString(last)));
}
LOG.fatal("LocalTransactionState Dump:\n" + ts.debug());
this.crash(ex);
}
} finally {
this.currentVoltProc = previous;
this.finishVoltProcedure(volt_proc);
if (hstore_conf.site.txn_profiling && ts.profiler != null) ts.profiler.startPost();
// if (cresponse.getStatus() == Status.ABORT_UNEXPECTED) {
// cresponse.getException().printStackTrace();
// }
}
// If this is a MapReduce job, then we can just ignore the ClientResponse
// and return immediately. The VoltMapReduceProcedure is responsible for storing
// the result at the proper location.
if (ts.isMapReduce()) {
return;
} else if (cresponse == null) {
assert(this.isShuttingDown()) : String.format("No ClientResponse for %s???", ts);
return;
}
// -------------------------------
// PROCESS RESPONSE AND FIGURE OUT NEXT STEP
// -------------------------------
Status status = cresponse.getStatus();
if (debug.val) {
LOG.debug(String.format("%s - Finished execution of transaction control code " +
"[status=%s, beforeMode=%s, currentMode=%s]",
ts, status, before_mode, this.currentExecMode));
if (ts.hasPendingError()) {
LOG.debug(String.format("%s - Txn finished with pending error: %s",
ts, ts.getPendingErrorMessage()));
}
}
// We assume that most transactions are not speculatively executed and are successful
// Therefore we don't want to grab the exec_mode lock here.
if (predict_singlePartition == false || this.canProcessClientResponseNow(ts, status, before_mode)) {
this.processClientResponse(ts, cresponse);
}
// Otherwise always queue our response, since we know that whatever thread is out there
// is waiting for us to finish before it drains the queued responses
else {
// If the transaction aborted, then we can't execute any transaction that touch the tables
// that this guy touches. But since we can't just undo this transaction without undoing
// everything that came before it, we'll just disable executing all transactions until the
// current distributed transaction commits
if (status != Status.OK && ts.isExecReadOnly(this.partitionId) == false) {
this.setExecutionMode(ts, ExecutionMode.DISABLED);
int blocked = this.work_queue.drainTo(this.currentBlockedTxns);
if (debug.val) {
if (trace.val && blocked > 0)
LOG.trace(String.format("Blocking %d transactions at partition %d because ExecutionMode is now %s",
blocked, this.partitionId, this.currentExecMode));
LOG.debug(String.format("Disabling execution on partition %d because speculative %s aborted",
this.partitionId, ts));
}
}
if (trace.val)
LOG.trace(String.format("%s - Queuing ClientResponse [status=%s, origMode=%s, newMode=%s, dtxn=%s]",
ts, cresponse.getStatus(), before_mode, this.currentExecMode, this.currentDtxn));
this.blockClientResponse(ts, cresponse);
}
}