package edu.brown.hstore.callbacks;
import org.apache.log4j.Logger;
import com.google.protobuf.RpcCallback;
import edu.brown.catalog.CatalogUtil;
import edu.brown.hstore.HStoreSite;
import edu.brown.hstore.Hstoreservice;
import edu.brown.hstore.Hstoreservice.SendDataResponse;
import edu.brown.hstore.Hstoreservice.Status;
import edu.brown.hstore.txns.AbstractTransaction;
import edu.brown.logging.LoggerUtil;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
/**
* This callback waits until all of the TransactionMapResponses have come
* back from all other partitions in the cluster. The unblockCallback will
* switch the MapReduceTransaction handle into the REDUCE phase and then requeue
* it at the local HStoreSite
* @author pavlo
*/
public class SendDataCallback extends BlockingRpcCallback<AbstractTransaction, SendDataResponse> {
private static final Logger LOG = Logger.getLogger(SendDataCallback.class);
private static final LoggerBoolean debug = new LoggerBoolean();
private static final LoggerBoolean trace = new LoggerBoolean();
static {
LoggerUtil.attachObserver(LOG, debug, trace);
}
private AbstractTransaction ts;
private final int num_sites;
/**
* Constructor
* @param hstore_site
*/
public SendDataCallback(HStoreSite hstore_site) {
super(hstore_site, false);
this.num_sites = CatalogUtil.getAllSites(hstore_site.getSite()).size();
}
public void init(AbstractTransaction ts, RpcCallback<AbstractTransaction> orig_callback) {
assert(this.isInitialized() == false) :
String.format("Trying to initialize %s twice! [origTs=%s, newTs=%s]",
this.getClass().getSimpleName(), this.ts, ts);
if (debug.val)
LOG.debug("Starting new " + this.getClass().getSimpleName() + " for " + ts);
this.ts = ts;
super.init(ts.getTransactionId(), this.num_sites, orig_callback);
}
@Override
protected void finishImpl() {
this.ts = null;
}
@Override
public boolean isInitialized() {
return (this.ts != null);
}
/**
* This gets invoked after all of the partitions have finished
* executing the map phase for this txn
*/
@Override
protected void unblockCallback() {
assert(this.isAborted() == false);
this.getOrigCallback().run(this.ts);
}
@Override
protected void abortCallback(Status status) {
assert(this.isInitialized()) : "ORIG TXN: " + this.getTransactionId();
assert(false) : "Unexpected: " + this.ts;
}
@Override
protected int runImpl(SendDataResponse response) {
if (debug.val)
LOG.debug(String.format("Got %s with status %s for %s",
response.getClass().getSimpleName(),
response.getStatus(),
this.ts));
assert(this.ts != null) :
String.format("Missing transaction handle for txn #%d", response.getTransactionId());
Long orig_txn_id = this.getTransactionId();
long resp_txn_id = response.getTransactionId();
Long ts_txn_id = this.ts.getTransactionId();
// If we get a response that matches our original txn but the LocalTransaction handle
// has changed, then we need to will just ignore it
if (orig_txn_id.longValue() == resp_txn_id && orig_txn_id.equals(ts_txn_id) == false) {
if (debug.val) LOG.debug(String.format("Ignoring %s for a different transaction #%d [origTxn=#%d]",
response.getClass().getSimpleName(), resp_txn_id, orig_txn_id));
return (0);
}
// Otherwise, make sure it's legit
assert(ts_txn_id == resp_txn_id) :
String.format("Unexpected %s for a different transaction %s != #%d [expected=#%d]",
response.getClass().getSimpleName(), this.ts, resp_txn_id, ts_txn_id);
if (response.getStatus() != Hstoreservice.Status.OK || this.isAborted()) {
this.abort(response.getStatus());
return (0);
}
if (debug.val) LOG.debug("SendDataCallback, I am trying to return 1, actually counter is:" + this.getCounter());
return 1;
}
}