package edu.brown.hstore.handlers;
import org.apache.log4j.Logger;
import org.voltdb.ParameterSet;
import org.voltdb.exceptions.ServerFaultException;
import org.voltdb.messaging.FastDeserializer;
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.TransactionInitRequest;
import edu.brown.hstore.Hstoreservice.TransactionInitResponse;
import edu.brown.hstore.Hstoreservice.WorkFragment;
import edu.brown.hstore.callbacks.RemoteInitQueueCallback;
import edu.brown.hstore.dispatchers.AbstractDispatcher;
import edu.brown.hstore.txns.AbstractTransaction;
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;
/**
* Add the given transaction id to this site's queue manager with all of the partitions that
* it needs to lock. This is only for distributed transactions.
* The callback will be invoked once the transaction has acquired all of the locks for the
* partitions provided, or aborted if the transaction is unable to lock those partitions.
* @author pavlo
*/
public class TransactionInitHandler extends AbstractTransactionHandler<TransactionInitRequest, TransactionInitResponse> {
private static final Logger LOG = Logger.getLogger(TransactionInitHandler.class);
private static final LoggerBoolean debug = new LoggerBoolean();
private static final LoggerBoolean trace = new LoggerBoolean();
static {
LoggerUtil.attachObserver(LOG, debug, trace);
}
private final AbstractDispatcher<Object[]> initDispatcher;
public TransactionInitHandler(HStoreSite hstore_site, HStoreCoordinator hstore_coord, AbstractDispatcher<Object[]> initDispatcher) {
super(hstore_site, hstore_coord);
this.initDispatcher = initDispatcher;
}
@Override
public void sendLocal(Long txn_id, TransactionInitRequest request, PartitionSet partitions, RpcCallback<TransactionInitResponse> callback) {
this.remoteQueue(null, request, callback);
}
@Override
public void sendRemote(HStoreService channel, ProtoRpcController controller, TransactionInitRequest request, RpcCallback<TransactionInitResponse> callback) {
channel.transactionInit(controller, request, callback);
}
@Override
public void remoteQueue(RpcController controller, TransactionInitRequest request,
RpcCallback<TransactionInitResponse> callback) {
if (initDispatcher != null) {
if (debug.val) LOG.debug("Queuing request for txn #" + request.getTransactionId());
Object o[] = { controller, request, callback };
initDispatcher.queue(o);
} else {
this.remoteHandler(controller, request, callback);
}
}
@Override
public void remoteHandler(RpcController controller, TransactionInitRequest request, RpcCallback<TransactionInitResponse> callback) {
assert(request.hasTransactionId()) : "Got " + request.getClass().getSimpleName() + " without a txn id!";
Long txn_id = request.getTransactionId();
if (debug.val)
LOG.debug(String.format("Got %s for txn #%d", request.getClass().getSimpleName(), txn_id));
AbstractTransaction ts = this.hstore_site.getTransaction(txn_id);
assert(ts == null || ts instanceof LocalTransaction) :
String.format("Got init request for remote txn #%d but we already have one [%s]",
txn_id, ts);
// This allocation is unnecessary if we're on the same site
PartitionSet partitions = null;
if (ts instanceof LocalTransaction) {
partitions = ((LocalTransaction)ts).getPredictTouchedPartitions();
} else {
// We first need all of the partitions so that we know
// what it's actually going to touch
// The init callback obviously only needs to have the
// partitions that are local at this site.
partitions = new PartitionSet(request.getPartitionsList());
ParameterSet procParams = null;
if (request.hasProcParams()) {
FastDeserializer fds = new FastDeserializer(request.getProcParams().asReadOnlyByteBuffer());
try {
procParams = fds.readObject(ParameterSet.class);
} catch (Exception ex) {
String msg = String.format("Failed to deserialize procedure ParameterSet for txn #%d from %s",
txn_id, request.getClass().getSimpleName());
throw new ServerFaultException(msg, ex, txn_id);
}
}
// If we don't have a handle, we need to make one so that we can stick in the
// things that we need to keep track of at this site. At this point we know that we're on
// a remote site from the txn's base partition
ts = this.hstore_site.getTransactionInitializer()
.createRemoteTransaction(txn_id,
partitions,
procParams,
request.getBasePartition(),
request.getProcedureId());
// Make sure that we initialize the RemoteTransactionInitCallback too!
RemoteInitQueueCallback initCallback = ts.getInitCallback();
initCallback.init((RemoteTransaction)ts, partitions, callback);
}
// If (request.getPrefetchFragmentsCount() > 0), then we need to
// make a RemoteTransaction handle for ourselves so that we can keep track of
// our state when prefetching queries.
if (request.getPrefetchFragmentsCount() > 0) {
// Stick the prefetch information into the transaction
if (debug.val) {
PartitionSet prefetchPartitions = new PartitionSet();
for (WorkFragment fragment : request.getPrefetchFragmentsList())
prefetchPartitions.add(fragment.getPartitionId());
LOG.debug(String.format("%s - Attaching %d prefetch %s at partitions %s",
ts, request.getPrefetchFragmentsCount(),
WorkFragment.class.getSimpleName(), prefetchPartitions));
}
// for (int i = 0; i < request.getPrefetchParamsCount(); i++) {
// LOG.info(String.format("%s - XXX INBOUND PREFETCH RAW [%02d]: %s",
// ts, i,
// StringUtil.md5sum(request.getPrefetchParams(i).asReadOnlyByteBuffer())));
// }
ts.initializePrefetch();
ts.attachPrefetchQueries(request.getPrefetchFragmentsList(),
request.getPrefetchParamsList());
}
// We don't need to send back a response right here.
// The init callback will wait until it has results from all of the partitions
// the tasks were sent to and then send back everything in a single response message
this.hstore_site.transactionInit(ts);
}
@Override
protected ProtoRpcController getProtoRpcController(LocalTransaction ts, int site_id) {
return ts.getTransactionInitController(site_id);
}
}