package edu.brown.hstore.handlers;
import org.apache.log4j.Logger;
import org.voltdb.ParameterSet;
import org.voltdb.VoltTable;
import org.voltdb.exceptions.ServerFaultException;
import org.voltdb.messaging.FastDeserializer;
import com.google.protobuf.ByteString;
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.TransactionWorkRequest;
import edu.brown.hstore.Hstoreservice.TransactionWorkResponse;
import edu.brown.hstore.Hstoreservice.WorkFragment;
import edu.brown.hstore.callbacks.RemoteWorkCallback;
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.CollectionUtil;
import edu.brown.utils.PartitionSet;
public class TransactionWorkHandler extends AbstractTransactionHandler<TransactionWorkRequest, TransactionWorkResponse> {
private static final Logger LOG = Logger.getLogger(TransactionWorkHandler.class);
private static final LoggerBoolean debug = new LoggerBoolean();
private static final LoggerBoolean trace = new LoggerBoolean();
static {
LoggerUtil.attachObserver(LOG, debug, trace);
}
public TransactionWorkHandler(HStoreSite hstore_site, HStoreCoordinator hstore_coord) {
super(hstore_site, hstore_coord);
}
@Override
public void sendLocal(Long txn_id, TransactionWorkRequest request, PartitionSet partitions, RpcCallback<TransactionWorkResponse> callback) {
// TODO
}
@Override
public void sendRemote(HStoreService channel, ProtoRpcController controller, TransactionWorkRequest request, RpcCallback<TransactionWorkResponse> callback) {
channel.transactionWork(controller, request, callback);
}
@Override
public void remoteQueue(RpcController controller, TransactionWorkRequest request,
RpcCallback<TransactionWorkResponse> callback) {
if (debug.val)
LOG.debug(String.format("Executing %s using remote handler for txn #%d",
request.getClass().getSimpleName(), request.getTransactionId()));
this.remoteHandler(controller, request, callback);
}
@Override
public void remoteHandler(RpcController controller, TransactionWorkRequest request,
RpcCallback<TransactionWorkResponse> 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 [partitionFragments=%d]",
request.getClass().getSimpleName(), txn_id, request.getFragmentsCount()));
RemoteTransaction ts = hstore_site.getTransaction(txn_id);
assert(ts != null);
assert(txn_id.equals(ts.getTransactionId())) :
String.format("Mismatched %s - Expected[%d] != Actual[%s]", ts, txn_id, ts.getTransactionId());
// Deserialize embedded ParameterSets and store it in the RemoteTransaction handle
// This way we only do it once per HStoreSite. This will also force us to avoid having
// to do it for local work
ParameterSet parameterSets[] = new ParameterSet[request.getParamsCount()]; // TODO: Cache!
for (int i = 0; i < parameterSets.length; i++) {
ByteString paramData = request.getParams(i);
if (paramData != null && paramData.isEmpty() == false) {
final FastDeserializer fds = new FastDeserializer(paramData.asReadOnlyByteBuffer());
if (trace.val)
LOG.trace(String.format("Txn #%d paramData[%d] => %s",
txn_id, i, fds.buffer()));
try {
parameterSets[i] = fds.readObject(ParameterSet.class);
} catch (Exception ex) {
String msg = String.format("Failed to deserialize ParameterSet[%d] for txn #%d TransactionRequest", i, txn_id);
throw new ServerFaultException(msg, ex, txn_id);
}
// LOG.info("PARAMETER[" + i + "]: " + parameterSets[i]);
} else {
parameterSets[i] = ParameterSet.EMPTY;
}
} // FOR
ts.attachParameterSets(parameterSets);
// Deserialize attached VoltTable input dependencies
FastDeserializer fds = null;
VoltTable vt = null;
for (int i = 0, cnt = request.getAttachedDataCount(); i < cnt; i++) {
int input_dep_id = request.getAttachedDepId(i);
ByteString data = request.getAttachedData(i);
if (data.isEmpty()) {
String msg = String.format("%s input dependency %d is empty", ts, input_dep_id);
LOG.warn(msg + "\n" + request);
throw new ServerFaultException(msg, txn_id);
}
if (fds == null) fds = new FastDeserializer(data.asReadOnlyByteBuffer());
else fds.setBuffer(data.asReadOnlyByteBuffer());
vt = null;
try {
vt = fds.readObject(VoltTable.class);
} catch (Exception ex) {
String msg = String.format("Failed to deserialize VoltTable[%d] for txn #%d", input_dep_id, txn_id);
throw new ServerFaultException(msg, ex, txn_id);
}
assert(vt != null);
ts.attachInputDependency(input_dep_id, vt);
} // FOR
// This is work from a transaction executing at another node
// Any other message can just be sent along to the ExecutionSite without sending
// back anything right away. The ExecutionSite will use our wrapped callback handle
// to send back whatever response it needs to, but we won't actually send it
// until we get back results from all of the partitions
// TODO: The base information of a set of FragmentTaskMessages should be moved into
// the message wrapper (e.g., base partition, client handle)
boolean first = true;
int fragmentCount = request.getFragmentsCount();
PartitionSet partitions = null;
if (fragmentCount == 1) {
WorkFragment work = CollectionUtil.first(request.getFragmentsList());
partitions = this.catalogContext.getPartitionSetSingleton(work.getPartitionId());
}
else {
partitions = new PartitionSet();
for (WorkFragment work : request.getFragmentsList()) {
partitions.add(work.getPartitionId());
} // FOR
}
for (WorkFragment work : request.getFragmentsList()) {
// Always initialize the TransactionWorkCallback for the first callback
if (first) {
RemoteWorkCallback work_callback = ts.getWorkCallback();
if (work_callback.isInitialized()) work_callback.finish(); // HACK
work_callback.init(ts, partitions, callback);
if (debug.val)
LOG.debug(String.format("Initializing %s for %s",
work_callback.getClass().getSimpleName(), ts));
}
if (debug.val)
LOG.debug(String.format("Invoking transactionWork for %s [first=%s]", ts, first));
hstore_site.transactionWork(ts, work);
first = false;
} // FOR
// We don't need to send back a response right here.
// TransactionWorkCallback 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
}
@Override
protected ProtoRpcController getProtoRpcController(LocalTransaction ts, int site_id) {
return ts.getTransactionWorkController(site_id);
}
}