package edu.brown.hstore.handlers; import org.apache.log4j.Logger; import com.google.protobuf.RpcCallback; import com.google.protobuf.RpcController; import edu.brown.hstore.HStoreCoordinator; import edu.brown.hstore.HStoreSite; import edu.brown.hstore.Hstoreservice.HStoreService; import edu.brown.hstore.Hstoreservice.TransactionPrepareRequest; import edu.brown.hstore.Hstoreservice.TransactionPrepareResponse; import edu.brown.hstore.callbacks.RemotePrepareCallback; import edu.brown.hstore.txns.LocalTransaction; import edu.brown.hstore.txns.RemoteTransaction; import edu.brown.logging.LoggerUtil; import edu.brown.logging.LoggerUtil.LoggerBoolean; import edu.brown.protorpc.ProtoRpcController; import edu.brown.utils.PartitionSet; public class TransactionPrepareHandler extends AbstractTransactionHandler<TransactionPrepareRequest, TransactionPrepareResponse> { private static final Logger LOG = Logger.getLogger(TransactionPrepareHandler.class); private static final LoggerBoolean debug = new LoggerBoolean(); static { LoggerUtil.attachObserver(LOG, debug); } public TransactionPrepareHandler(HStoreSite hstore_site, HStoreCoordinator hstore_coord) { super(hstore_site, hstore_coord); } @Override public void sendLocal(Long txn_id, TransactionPrepareRequest request, PartitionSet partitions, RpcCallback<TransactionPrepareResponse> callback) { // We don't care whether we actually updated anybody locally, so we don't need to // pass in a set to get the partitions that were updated here. LocalTransaction ts = this.hstore_site.getTransaction(txn_id); assert(ts != null) : "Unexpected null transaction handle for txn #" + txn_id; this.hstore_site.transactionPrepare(ts, partitions, ts.getPrepareCallback()); } @Override public void sendRemote(HStoreService channel, ProtoRpcController controller, TransactionPrepareRequest request, RpcCallback<TransactionPrepareResponse> callback) { channel.transactionPrepare(controller, request, callback); } @Override public void remoteQueue(RpcController controller, TransactionPrepareRequest request, RpcCallback<TransactionPrepareResponse> callback) { if (debug.val) LOG.debug(String.format("Sending %s to remote handler for txn #%d", request.getClass().getSimpleName(), request.getTransactionId())); this.remoteHandler(controller, request, callback); } @Override public void remoteHandler(RpcController controller, TransactionPrepareRequest request, RpcCallback<TransactionPrepareResponse> callback) { assert(request.hasTransactionId()) : "Got " + request.getClass().getSimpleName() + " without a txn id!"; Long txn_id = Long.valueOf(request.getTransactionId()); if (debug.val) LOG.debug(String.format("Got %s for txn #%d", request.getClass().getSimpleName(), txn_id)); // HACK // Use a TransactionPrepareWrapperCallback to ensure that we only send back // the prepare response once all of the PartitionExecutors have successfully // acknowledged that we're ready to commit PartitionSet partitions = new PartitionSet(request.getPartitionsList()); assert(partitions.isEmpty() == false) : "Unexpected empty list of updated partitions for txn #" + txn_id; partitions.retainAll(hstore_site.getLocalPartitionIds()); RemoteTransaction ts = this.hstore_site.getTransaction(txn_id); assert(ts != null) : "Unexpected null transaction handle for txn #" + txn_id; // Always create a new prepare callback because we may get multiple messages // to prepare this txn for commit. RemotePrepareCallback wrapper = ts.getPrepareCallback(); assert(wrapper.isInitialized() == false); wrapper.init(ts, partitions, callback); this.hstore_site.transactionPrepare(ts, partitions, wrapper); } @Override protected ProtoRpcController getProtoRpcController(LocalTransaction ts, int site_id) { return ts.getTransactionPrepareController(site_id); } }