status != Status.ABORT_RESTART &&
status != Status.ABORT_SPECULATIVE &&
status != Status.ABORT_EVICTEDACCESS) {
// Figure out whether this transaction should be redirected based on what partitions it
// tried to touch before it was aborted
FastIntHistogram touched = orig_ts.getTouchedPartitions();
// XXX: We should probably decrement the base partition by one
// so that we only consider where they actually executed queries
if (debug.val)
LOG.debug(String.format("Touched partitions for mispredicted %s\n%s",
orig_ts, touched));
int redirect_partition = HStoreConstants.NULL_PARTITION_ID;
if (touched.getValueCount() == 1) {
redirect_partition = touched.getMaxValue();
}
// If the original base partition is in our most touched set, then
// we'll prefer to use that
else if (touched.getValueCount() > 0) {
Collection<Integer> most_touched = touched.getMaxCountValues();
assert(most_touched != null) :
"Failed to get most touched partition for " + orig_ts + "\n" + touched;
if (debug.val)
LOG.debug(String.format("Most touched partitions for mispredicted %s: %s",
orig_ts, most_touched));
if (most_touched.contains(base_partition)) {
redirect_partition = base_partition;
} else {
redirect_partition = CollectionUtil.random(most_touched);
}
}
else {
redirect_partition = base_partition;
}
assert(redirect_partition != HStoreConstants.NULL_PARTITION_ID) :
"Redirect partition is null!\n" + orig_ts.debug();
if (debug.val) {
LOG.debug("Redirect Partition: " + redirect_partition + " -> " + (this.isLocalPartition(redirect_partition) == false));
LOG.debug("Local Partitions: " + this.local_partitions);
}
// If the txn wants to execute on another node, then we'll send them off *only* if this txn wasn't
// already redirected at least once. If this txn was already redirected, then it's going to just
// execute on the same partition, but this time as a multi-partition txn that locks all partitions.
// That's what you get for messing up!!
if (this.isLocalPartition(redirect_partition) == false && orig_ts.getRestartCounter() == 0) {
if (debug.val)
LOG.debug(String.format("%s - Redirecting to partition %d because of misprediction",
orig_ts, redirect_partition));
Procedure catalog_proc = orig_ts.getProcedure();
StoredProcedureInvocation spi = new StoredProcedureInvocation(orig_ts.getClientHandle(),
catalog_proc.getId(),
catalog_proc.getName(),
orig_ts.getProcedureParameters().toArray());
spi.setBasePartition(redirect_partition);
spi.setRestartCounter(orig_ts.getRestartCounter()+1);
FastSerializer out = this.outgoingSerializers.get();
try {
out.writeObject(spi);
} catch (IOException ex) {
String msg = "Failed to serialize StoredProcedureInvocation to redirect txn";
throw new ServerFaultException(msg, ex, orig_ts.getTransactionId());
}
RedirectCallback callback;
try {
// callback = (RedirectCallback)objectPools.CALLBACKS_TXN_REDIRECT_REQUEST.borrowObject();
callback = new RedirectCallback(this);
callback.init(orig_ts.getClientCallback());
} catch (Exception ex) {
String msg = "Failed to get TransactionRedirectCallback";
throw new ServerFaultException(msg, ex, orig_ts.getTransactionId());
}
this.hstore_coordinator.transactionRedirect(out.getBytes(),
callback,
redirect_partition);
out.clear();
if (hstore_conf.site.txn_counters) TransactionCounter.REDIRECTED.inc(orig_ts.getProcedure());
return (Status.ABORT_RESTART);
// Allow local redirect
} else if (orig_ts.getRestartCounter() <= 1) {
if (redirect_partition != base_partition &&
this.isLocalPartition(redirect_partition)) {
if (debug.val)
LOG.debug(String.format("%s - Redirecting to local partition %d [restartCtr=%d]%s",
orig_ts, redirect_partition, orig_ts.getRestartCounter(),
(trace.val ? "\n"+touched : "")));
base_partition = redirect_partition;
}
} else {
if (debug.val)
LOG.debug(String.format("%s - Mispredicted txn has already been aborted once before. " +
"Restarting as all-partition txn [restartCtr=%d, redirectPartition=%d]\n%s",
orig_ts, orig_ts.getRestartCounter(), redirect_partition, touched));
touched.put(this.local_partitions);
}
}
// -------------------------------
// LOCAL RE-EXECUTION