package edu.brown.hstore.callbacks;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.List;
import org.apache.log4j.Logger;
import org.voltdb.messaging.FastDeserializer;
import com.google.protobuf.RpcCallback;
import edu.brown.hstore.HStoreSite;
import edu.brown.hstore.TransactionQueueManager;
import edu.brown.hstore.Hstoreservice.Status;
import edu.brown.hstore.Hstoreservice.TransactionInitResponse;
import edu.brown.hstore.specexec.PrefetchQueryUtil;
import edu.brown.hstore.txns.LocalTransaction;
import edu.brown.logging.LoggerUtil;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
import edu.brown.utils.PartitionSet;
import edu.brown.utils.StringUtil;
/**
* InitQueue Callback on the Txn's Local HStoreSite
* @author pavlo
*/
public class LocalInitQueueCallback extends PartitionCountingCallback<LocalTransaction> implements RpcCallback<TransactionInitResponse> {
private static final Logger LOG = Logger.getLogger(LocalInitQueueCallback.class);
private static final LoggerBoolean debug = new LoggerBoolean();
static {
LoggerUtil.attachObserver(LOG, debug);
}
private final boolean prefetch;
private ThreadLocal<FastDeserializer> prefetchDeserializers;
private final TransactionQueueManager txnQueueManager;
private final List<TransactionInitResponse> responses = new ArrayList<TransactionInitResponse>();
// ----------------------------------------------------------------------------
// INTIALIZATION
// ----------------------------------------------------------------------------
public LocalInitQueueCallback(HStoreSite hstore_site) {
super(hstore_site);
this.prefetch = hstore_site.getHStoreConf().site.exec_prefetch_queries;
this.txnQueueManager = hstore_site.getTransactionQueueManager();
}
@Override
public void init(LocalTransaction ts, PartitionSet partitions) {
this.responses.clear();
super.init(ts, partitions);
}
// ----------------------------------------------------------------------------
// CALLBACK METHODS
// ----------------------------------------------------------------------------
@Override
public void run(int partition) {
if (debug.val)
LOG.debug(String.format("%s - Prefetch=%s / HasPrefetchFragments=%s",
this.ts, this.prefetch, this.ts.hasPrefetchFragments()));
if (this.prefetch &&
this.ts.hasPrefetchFragments() &&
partition != this.ts.getBasePartition() &&
hstore_site.isLocalPartition(partition)) {
if (debug.val)
LOG.debug(String.format("%s - Checking for prefetch queries at partition %d",
this.ts, partition));
if (this.prefetchDeserializers == null) {
synchronized (this) {
this.prefetchDeserializers = new ThreadLocal<FastDeserializer>() {
@Override
protected FastDeserializer initialValue() {
return (new FastDeserializer(new byte[0]));
}
};
} // SYNCH
}
FastDeserializer fd = this.prefetchDeserializers.get();
boolean result = PrefetchQueryUtil.dispatchPrefetchQueries(hstore_site, this.ts, fd, partition);
if (debug.val)
LOG.debug(String.format("%s - Result from dispatching prefetch queries at partition %d -> %s",
this.ts, partition, result));
}
super.run(partition);
}
@Override
protected void unblockCallback() {
assert(this.isAborted() == false) :
"Trying unblock " + this.ts + " but it was already marked as aborted";
// HACK: If this is a single-partition txn, then we don't
// need to submit it for execution because the PartitionExecutor
// will fire it off right away
if (this.ts.isPredictSinglePartition() == false) {
if (debug.val) LOG.debug(this.ts + " is ready to execute. Passing to HStoreSite");
this.hstore_site.transactionStart(this.ts);
}
}
@Override
protected void abortCallback(int partition, Status status) {
// If the transaction needs to be restarted, then we'll attempt to requeue it.
switch (status) {
case ABORT_SPECULATIVE:
case ABORT_RESTART:
// We don't care whether our transaction was rejected or not because we
// know that we still need to call TransactionFinish, which will delete
// the final transaction state
this.txnQueueManager.restartTransaction(this.ts, status);
break;
case ABORT_REJECT:
this.hstore_site.transactionReject(this.ts, status);
break;
default:
throw new RuntimeException(String.format("Unexpected status %s for %s", status, this.ts));
} // SWITCH
// this.cancel();
}
// ----------------------------------------------------------------------------
// RPC CALLBACK
// ----------------------------------------------------------------------------
@Override
public void run(TransactionInitResponse response) {
if (debug.val)
LOG.debug(String.format("%s - Got %s with status %s from partitions %s",
this.ts, response.getClass().getSimpleName(),
response.getStatus(), response.getPartitionsList()));
this.responses.add(response);
if (response.getStatus() != Status.OK) {
int reject_partition = response.getRejectPartition();
for (int partition : response.getPartitionsList()) {
if (partition != reject_partition) {
this.decrementCounter(partition);
}
} // FOR
// The last one should call the actual abort
this.abort(reject_partition, response.getStatus());
} else {
for (Integer partition : response.getPartitionsList()) {
this.run(partition.intValue());
} // FOR
}
}
@Override
public String toString() {
String ret = super.toString();
if (this.responses.isEmpty() == false) {
ret += "\n-------------\n";
String debug = "";
while (true) {
try {
debug = StringUtil.join("\n", this.responses);
} catch (ConcurrentModificationException ex) {
continue;
}
break;
}
ret += debug;
}
return (ret);
}
}