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.txns.AbstractTransaction;
import edu.brown.hstore.txns.LocalTransaction;
import edu.brown.logging.LoggerUtil;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
/**
* Special BlockingCallback wrapper for transactions. This class has utility methods for
* identifying when it is safe to delete a transaction handle from our local HStoreSite.
* @author pavlo
* @param <X> The type of AbstractTransaction handle that this callback uses
* @param <T> The message type of the original RpcCallback
* @param <U> The message type that we will accumulate before invoking the original RpcCallback
*/
@Deprecated
public abstract class AbstractTransactionCallback<X extends AbstractTransaction, T, U> extends BlockingRpcCallback<T, U> {
private static final Logger LOG = Logger.getLogger(AbstractTransactionCallback.class);
private static final LoggerBoolean debug = new LoggerBoolean();
private static final LoggerBoolean trace = new LoggerBoolean();
static {
LoggerUtil.attachObserver(LOG, debug, trace);
}
/**
* The current transaction handle that this callback is assigned to
*/
protected X ts;
/**
* This flag is set to true after the unblockCallback() invocation is finished
* This prevents somebody from checking whether we have invoked the unblock callback
* but are still in the middle of processing it.
*/
private boolean unblockFinished = false;
/**
* This flag is set to true after the abortCallback() invocation is finished
* This prevents somebody from checking whether we have invoked the abort callback
* but are still in the middle of processing it.
*/
private boolean abortFinished = false;
/**
* Constructor
* @param hstore_site
*/
protected AbstractTransactionCallback(HStoreSite hstore_site) {
super(hstore_site, false);
}
protected void init(X ts, int counter_val, RpcCallback<T> orig_callback) {
assert(ts != null) :
String.format("Null transaction handle in %s", this.getClass().getSimpleName());
assert(ts.isInitialized()) :
String.format("Uninitialized transaction handle in %s", this.getClass().getSimpleName());
this.ts = ts;
if (debug.val) LOG.debug(this.ts + " - Initializing new " + this.getClass().getSimpleName());
super.init(this.ts.getTransactionId(), counter_val, orig_callback);
}
@Override
protected void finishImpl() {
this.ts = null;
this.unblockFinished = false;
this.abortFinished = false;
}
@Override
public final boolean isInitialized() {
return (this.ts != null);
}
@Override
protected final void unblockCallback() {
assert(this.isUnblocked());
assert(this.ts != null) :
String.format("Null transaction handle for txn #%s in %s [lastTxn=%s / counter=%d/%d]",
this.getTransactionId(), this.getClass().getSimpleName(),
this.getOrigTransactionId(), this.getCounter(), this.getOrigCounter());
assert(this.ts.isInitialized()) :
String.format("Uninitialized transaction handle for txn #%s in %s [lastTxn=%s / origCounter=%d/%d]",
this.getTransactionId(), this.getClass().getSimpleName(),
this.getOrigTransactionId(), this.getCounter(), this.getOrigCounter());
if (this.isAborted() == false) {
this.unblockTransactionCallback();
}
this.unblockFinished = true;
}
@Override
protected final void abortCallback(Status status) {
assert(this.isAborted());
// We can't abort if we've already invoked the regular callback
boolean finish = false;
if (this.isUnblocked() == false) {
finish = this.abortTransactionCallback(status);
}
// If we abort, then we have to send out an ABORT to
// all of the partitions that we originally sent INIT requests too
// Note that we do this *even* if we haven't heard back from the remote
// HStoreSite that they've acknowledged our transaction
// We don't care when we get the response for this
if (finish && this.ts.isPredictSinglePartition() == false) {
if (this.ts instanceof LocalTransaction) {
this.finishTransaction(status);
} else {
// FIXME
}
}
this.abortFinished = true;
this.hstore_site.queueDeleteTransaction(this.txn_id, status);
}
@SuppressWarnings("unchecked")
@Override
public void cancel() {
super.cancel();
if (this.getOrigCallback() != null) {
((BlockingRpcCallback<T, U>)this.getOrigCallback()).cancel();
}
}
/**
* Transaction unblocking callback implementation
* @return
*/
protected abstract void unblockTransactionCallback();
/**
* Transaction abort callback implementation
* If this returns true, then we will invoke finishTransaction()
* @return
*/
protected abstract boolean abortTransactionCallback(Status status);
// ----------------------------------------------------------------------------
// CALLBACK CHECKS
// ----------------------------------------------------------------------------
/**
* Returns true if either the unblock or abort callbacks have been invoked
* and have finished their processing
*/
public final boolean allCallbacksFinished() {
if (this.isCanceled() == false && this.isInitialized()) {
if (this.getCounter() != 0) return (false);
return ((this.isUnblocked() && this.unblockFinished) || (this.isAborted() && this.abortFinished));
}
return (true);
}
// ----------------------------------------------------------------------------
// INTERNAL UTILITY METHODS
// ----------------------------------------------------------------------------
/**
* Tell the HStoreCoordinator to invoke the TransactionFinish process
* @param status
*/
protected final void finishTransaction(Status status) {
assert(this.ts != null) :
"Null transaction handle for txn #" + this.getTransactionId();
if (debug.val) LOG.debug(String.format("%s - Invoking TransactionFinish protocol from %s [status=%s]",
this.ts, this.getClass().getSimpleName(), status));
// Let everybody know that the party is over!
if (this.ts instanceof LocalTransaction) {
LocalTransaction local_ts = (LocalTransaction)this.ts;
LocalFinishCallback callback = ((LocalTransaction)this.ts).getFinishCallback();
callback.init(local_ts, status);
this.hstore_site.getCoordinator().transactionFinish(local_ts, status, callback);
}
}
@Override
public String toString() {
return String.format("%s / %s / Deletable=%s",
super.toString(), this.ts, this.allCallbacksFinished());
}
}