package edu.brown.hstore.callbacks; import org.apache.log4j.Logger; import com.google.protobuf.RpcCallback; import edu.brown.hstore.HStoreSite; import edu.brown.hstore.Hstoreservice.Status; import edu.brown.hstore.Hstoreservice.TransactionPrepareResponse; import edu.brown.hstore.txns.RemoteTransaction; import edu.brown.logging.LoggerUtil; import edu.brown.logging.LoggerUtil.LoggerBoolean; import edu.brown.utils.PartitionSet; /** * This is callback is used on the remote side of a TransactionPrepareRequest * so that the network-outbound callback is not invoked until all of the partitions * at this HStoreSite are finished preparing the transaction. * @author pavlo */ public class RemotePrepareCallback extends PartitionCountingCallback<RemoteTransaction> { private static final Logger LOG = Logger.getLogger(RemotePrepareCallback.class); private static final LoggerBoolean debug = new LoggerBoolean(); private static final LoggerBoolean trace = new LoggerBoolean(); static { LoggerUtil.attachObserver(LOG, debug, trace); } private final PartitionSet localPartitions = new PartitionSet(); private TransactionPrepareResponse.Builder builder = null; private RpcCallback<TransactionPrepareResponse> origCallback; // ---------------------------------------------------------------------------- // INTIALIZATION // ---------------------------------------------------------------------------- public RemotePrepareCallback(HStoreSite hstore_site) { super(hstore_site); } public void init(RemoteTransaction ts, PartitionSet partitions, RpcCallback<TransactionPrepareResponse> origCallback) { this.builder = TransactionPrepareResponse.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 protected void unblockCallback() { if (debug.val) LOG.debug(String.format("%s - Checking whether we can send back %s with status %s", this.ts, TransactionPrepareResponse.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, TransactionPrepareResponse.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, TransactionPrepareResponse.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()); 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, TransactionPrepareResponse.class.getSimpleName())); } } }