package edu.brown.hstore.callbacks; import org.apache.log4j.Logger; import org.voltdb.messaging.FastDeserializer; import com.google.protobuf.RpcCallback; import edu.brown.hstore.HStoreSite; import edu.brown.hstore.Hstoreservice.Status; import edu.brown.hstore.Hstoreservice.TransactionInitResponse; import edu.brown.hstore.specexec.PrefetchQueryUtil; import edu.brown.hstore.txns.RemoteTransaction; import edu.brown.logging.LoggerUtil; import edu.brown.logging.LoggerUtil.LoggerBoolean; import edu.brown.utils.PartitionSet; /** * * @author pavlo */ public class RemoteInitQueueCallback extends PartitionCountingCallback<RemoteTransaction> { private static final Logger LOG = Logger.getLogger(RemoteInitQueueCallback.class); private static final LoggerBoolean debug = new LoggerBoolean(); private static final LoggerBoolean trace = new LoggerBoolean(); static { LoggerUtil.attachObserver(LOG, debug, trace); } private final boolean prefetch; private ThreadLocal<FastDeserializer> prefetchDeserializers; private final PartitionSet localPartitions = new PartitionSet(); private TransactionInitResponse.Builder builder = null; private RpcCallback<TransactionInitResponse> origCallback; // ---------------------------------------------------------------------------- // INTIALIZATION // ---------------------------------------------------------------------------- public RemoteInitQueueCallback(HStoreSite hstore_site) { super(hstore_site); this.prefetch = hstore_site.getHStoreConf().site.exec_prefetch_queries; } public void init(RemoteTransaction ts, PartitionSet partitions, RpcCallback<TransactionInitResponse> origCallback) { this.builder = TransactionInitResponse.newBuilder() .setTransactionId(ts.getTransactionId().longValue()) .setStatus(Status.OK); this.origCallback = origCallback; // Remove non-local partitions this.localPartitions.clear(); this.localPartitions.addAll(partitions); this.localPartitions.retainAll(this.hstore_site.getLocalPartitionIds()); super.init(ts, this.localPartitions); } // ---------------------------------------------------------------------------- // CALLBACK METHODS // ---------------------------------------------------------------------------- @Override public void run(int partition) { if (trace.val) LOG.trace(String.format("%s - Prefetch=%s / HasPrefetchFragments=%s", this.ts, this.prefetch, this.ts.hasPrefetchFragments())); if (this.prefetch && this.ts.hasPrefetchFragments()) { if (this.prefetchDeserializers == null) { if (debug.val) LOG.debug(String.format("%s - Checking for prefetch queries at partition %d", this.ts, partition)); 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() { if (debug.val) LOG.debug(String.format("%s - Checking whether we can send back %s with status %s", this.ts, TransactionInitResponse.class.getSimpleName(), (this.builder != null ? this.builder.getStatus() : "???"))); if (this.builder != null) { if (debug.val) LOG.debug(String.format("%s - Sending %s to %s with status %s", this.ts, TransactionInitResponse.class.getSimpleName(), this.origCallback.getClass().getSimpleName(), this.builder.getStatus())); // Ok so where's what going on here. We need to send back // an abort message, so we're going use the builder that we've been // working on and send out the bomb back to the base partition tells it that this // transaction is kaput at this HStoreSite. this.builder.clearPartitions(); PartitionSet partitions = this.getPartitions(); for (int partition : partitions.values()) { assert(this.hstore_site.isLocalPartition(partition)); this.builder.addPartitions(partition); } // FOR assert(this.builder.getPartitionsList() != null) : String.format("The %s for %s has no results but it was suppose to have %d.", builder.getClass().getSimpleName(), this.ts, this.getOrigCounter()); assert(this.getOrigCounter() == this.builder.getPartitionsCount()) : String.format("The %s for %s has results from %d partitions but it was suppose to have %d.", builder.getClass().getSimpleName(), this.ts, builder.getPartitionsCount(), this.getOrigCounter()); assert(this.origCallback != null) : String.format("The original callback for %s is null!", this.ts); this.origCallback.run(this.builder.build()); this.builder = null; } else if (debug.val) { LOG.warn(String.format("%s - No builder is available? Unable to send back %s", this.ts, TransactionInitResponse.class.getSimpleName())); } } @Override protected void abortCallback(int partition, Status status) { // Uh... this might have already been sent out? if (this.builder != null) { if (debug.val) LOG.debug(String.format("%s - Aborting %s with status %s", this.ts, this.getClass().getSimpleName(), status)); // Ok so where's what going on here. We need to send back // an abort message, so we're going use the builder that we've been // working on and send out the bomb back to the base partition tells it that this // transaction is kaput at this HStoreSite. this.builder.setStatus(status); this.builder.clearPartitions(); this.builder.addAllPartitions(this.getPartitions()); this.builder.setRejectPartition(partition); assert(this.origCallback != null) : String.format("The original callback for %s is null!", this.ts); this.origCallback.run(this.builder.build()); this.builder = null; } else if (debug.val) { LOG.warn(String.format("%s - No builder is available? Unable to send back %s", this.ts, TransactionInitResponse.class.getSimpleName())); } } }