package edu.brown.hstore.callbacks;
import org.apache.log4j.Logger;
import com.google.protobuf.RpcCallback;
import edu.brown.hstore.HStoreSite;
import edu.brown.hstore.Hstoreservice.Status;
import edu.brown.hstore.Hstoreservice.TransactionFinishResponse;
import edu.brown.hstore.txns.LocalTransaction;
import edu.brown.logging.LoggerUtil;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
/**
* This callback waits until we have heard back from all of the partitions
* that they have finished processing our transaction. Once we get all of the
* acknowledgments that we need (including any local partitions), then
* we will queue this transaction up for deletion. If the <b>needs_requeue</b> flag
* is set to true, then we will requeue it first before deleting
* @author pavlo
*/
public class LocalFinishCallback extends PartitionCountingCallback<LocalTransaction> implements RpcCallback<TransactionFinishResponse> {
private static final Logger LOG = Logger.getLogger(LocalFinishCallback.class);
private static final LoggerBoolean debug = new LoggerBoolean();
static {
LoggerUtil.attachObserver(LOG, debug);
}
private Status status;
private boolean needs_requeue = false;
// ----------------------------------------------------------------------------
// INTIALIZATION
// ----------------------------------------------------------------------------
/**
* Constructor
* @param hstore_site
*/
public LocalFinishCallback(HStoreSite hstore_site) {
super(hstore_site);
}
public void init(LocalTransaction ts, Status status) {
this.status = status;
this.needs_requeue = false;
super.init(ts, ts.getPredictTouchedPartitions());
}
// ----------------------------------------------------------------------------
// UTILITY METHODS
// ----------------------------------------------------------------------------
/**
* Mark that txn for this callback needs to be requeued before it gets
* deleted once it gets responses from all of the partitions
* participating in it
*/
public void markForRequeue() {
assert(this.needs_requeue == false);
this.needs_requeue = true;
}
// ----------------------------------------------------------------------------
// CALLBACK METHODS
// ----------------------------------------------------------------------------
@Override
protected void unblockCallback() {
if (this.needs_requeue) {
this.hstore_site.transactionRequeue(this.ts, this.status);
}
// HACK: Just cancel out the InitCallback because we may never
// get back responses.
this.ts.getInitCallback().cancel();
try {
this.hstore_site.queueDeleteTransaction(this.ts.getTransactionId(), this.status);
} catch (Throwable ex) {
String msg = String.format("Failed to queue %s for deletion from %s",
ts, this.getClass().getSimpleName());
throw new RuntimeException(msg, ex);
}
}
@Override
protected void abortCallback(int partition, Status status) {
String msg = String.format("Invalid State for %s: Trying to abort a finished transaction [status=%s]",
this.ts, status);
throw new RuntimeException(msg);
}
// ----------------------------------------------------------------------------
// RPC CALLBACK
// ----------------------------------------------------------------------------
@Override
public void run(TransactionFinishResponse response) {
if (debug.val)
LOG.debug(String.format("%s - Got %s with %s [partitions=%s, counter=%d]",
this.ts, response.getClass().getSimpleName(),
this.status, response.getPartitionsList(), this.getCounter()));
assert(this.ts != null) :
String.format("Missing LocalTransaction handle for txn #%d [status=%s]",
response.getTransactionId(), this.status);
// Any response has to match our current transaction handle
assert(this.ts.getTransactionId().longValue() == response.getTransactionId()) :
String.format("Unexpected %s for a different transaction %s != #%d",
response.getClass().getSimpleName(), this.ts, response.getTransactionId());
for (Integer partition : response.getPartitionsList()) {
this.run(partition.intValue());
} // FOR
return;
}
}