package edu.brown.hstore.callbacks;
import java.nio.ByteBuffer;
import org.apache.log4j.Logger;
import org.voltdb.ClientResponseImpl;
import org.voltdb.VoltTable;
import org.voltdb.messaging.FastDeserializer;
import edu.brown.hstore.HStoreSite;
import edu.brown.hstore.Hstoreservice.Status;
import edu.brown.hstore.Hstoreservice.TransactionReduceResponse;
import edu.brown.hstore.Hstoreservice.TransactionReduceResponse.ReduceResult;
import edu.brown.hstore.txns.MapReduceTransaction;
import edu.brown.logging.LoggerUtil;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
import edu.brown.utils.StringUtil;
/**
* This callback waits until all of the TransactionMapResponses have come
* back from all other partitions in the cluster.
* @author pavlo
*/
public class TransactionReduceCallback extends AbstractTransactionCallback<MapReduceTransaction, TransactionReduceResponse, TransactionReduceResponse> {
private static final Logger LOG = Logger.getLogger(TransactionReduceCallback.class);
private static final LoggerBoolean debug = new LoggerBoolean();
private static final LoggerBoolean trace = new LoggerBoolean();
static {
LoggerUtil.attachObserver(LOG, debug, trace);
}
private final VoltTable finalResults[];
/**
* Constructor
* @param hstore_site
*/
public TransactionReduceCallback(HStoreSite hstore_site) {
super(hstore_site);
this.finalResults = new VoltTable[hstore_site.getCatalogContext().numberOfPartitions];
}
public void init(MapReduceTransaction ts) {
super.init(ts, ts.getPredictTouchedPartitions().size(), null);
}
@Override
protected void finishImpl() {
super.finishImpl();
for (int i = 0; i < this.finalResults.length; i++)
this.finalResults[i] = null;
}
/**
* This gets invoked after all of the partitions have finished
* executing the map phase for this txn
*/
@Override
protected void unblockTransactionCallback() {
if (debug.val)
LOG.debug(ts + " is ready to execute. Passing to HStoreSite");
// Client gets the final result, and txn is about to finish
// STEP 1
// Send the final result from all the partitions for this MR job
// back to the client.
ClientResponseImpl cresponse = new ClientResponseImpl();
cresponse.init(ts, Status.OK, this.finalResults, "");
hstore_site.responseSend(ts, cresponse);
if (hstore_site.getHStoreConf().site.mr_map_blocking) {
// STEP 2
// Initialize the FinishCallback and tell every partition in the cluster
// to clean up this transaction because we're done with it!
this.finishTransaction(Status.OK);
}
}
@Override
protected boolean abortTransactionCallback(Status status) {
assert(this.isInitialized()) : "ORIG TXN: " + this.getTransactionId();
return (true);
}
@Override
protected int runImpl(TransactionReduceResponse response) {
if (debug.val)
LOG.debug(String.format("Got %s with status %s for %s [partitions=%s]",
response.getClass().getSimpleName(),
response.getStatus(),
this.ts,
response.getResultsList()));
assert(this.ts != null) :
String.format("Missing LocalTransaction handle for txn #%d [status=%s]",
response.getTransactionId(), response.getStatus());
// Otherwise, make sure it's legit
assert(this.ts.getTransactionId().longValue() == response.getTransactionId()) :
String.format("Unexpected %s for a different transaction %s != #%d [expected=#%d]",
response.getClass().getSimpleName(), this.ts, response.getTransactionId(), this.getTransactionId());
if (response.getStatus() != Status.OK || this.isAborted()) {
this.abort(response.getStatus());
} else {
// Here we should receive the reduceOutput data
for (ReduceResult pr : response.getResultsList()) {
int partition = pr.getPartitionId();
ByteBuffer bs = pr.getData().asReadOnlyByteBuffer();
VoltTable vt = null;
try {
vt = FastDeserializer.deserialize(bs, VoltTable.class);
} catch (Exception ex) {
throw new RuntimeException("Unexpected error when deserializing VoltTable", ex);
}
assert(vt != null);
if (debug.val) {
byte bytes[] = pr.getData().toByteArray();
LOG.debug(String.format("Inbound Partition reduce result for Partition #%d: RowCount=%d / MD5=%s / Length=%d",
partition, vt.getRowCount(),StringUtil.md5sum(bytes), bytes.length));
}
this.finalResults[partition] = vt;
} // FOR
}
return (response.getResultsCount());
}
}