package edu.brown.hstore.txns;
import java.util.BitSet;
import java.util.concurrent.atomic.AtomicBoolean;
import org.voltdb.CatalogContext;
import edu.brown.hstore.HStoreSite;
import edu.brown.hstore.callbacks.LocalFinishCallback;
import edu.brown.hstore.callbacks.LocalPrepareCallback;
import edu.brown.pools.Poolable;
import edu.brown.protorpc.ProtoRpcController;
import edu.brown.utils.PartitionSet;
/**
* Container class for all of the objects needed by a distributed txn
* @author pavlo
*/
public class DistributedState implements Poolable {
/**
* Current txn
*/
private LocalTransaction ts = null;
/**
* The partitions that we notified that we are done with them
*/
protected final PartitionSet exec_donePartitions = new PartitionSet();
/**
*
*/
protected final BitSet notified_prepare;
/**
* We need to make sure that we only blast out a finish
* notification once per transaction.
*/
protected final AtomicBoolean notified_finish = new AtomicBoolean(false);
/**
* If true, then all of the partitions that this txn needs is on the same
* site as the base partition
*/
protected boolean is_all_local = true;
/**
* If this is a distributed transaction and we are doing aggressive spec exec,
* then this bit map is used to keep track whether we have sent the Procedure
* ParameterSet to a remote site.
*/
protected final BitSet sent_parameters;
/**
* Cached ProtoRpcControllers
* SiteId -> Controller
*/
private final ProtoRpcController rpc_transactionInit[];
private final ProtoRpcController rpc_transactionWork[];
// private final ProtoRpcController rpc_transactionPrepare[];
private final ProtoRpcController rpc_transactionFinish[];
// ----------------------------------------------------------------------------
// CALLBACKS
// ----------------------------------------------------------------------------
/**
* This callback is used to keep track of what partitions have replied that they are
* ready to commit/abort our transaction.
* This is only needed for distributed transactions.
*/
protected final LocalPrepareCallback prepare_callback;
/**
* This callback will keep track of whether we have gotten all the 2PC acknowledgments
* from the remote partitions. Once this is finished, we can then invoke
* HStoreSite.deleteTransaction()
*/
protected final LocalFinishCallback finish_callback;
// ----------------------------------------------------------------------------
// INITIALIZATION
// ----------------------------------------------------------------------------
/**
* Constructor
* @param hstore_site
*/
public DistributedState(HStoreSite hstore_site) {
CatalogContext catalogContext = hstore_site.getCatalogContext();
this.notified_prepare = new BitSet(catalogContext.numberOfPartitions);
this.sent_parameters = new BitSet(catalogContext.numberOfSites);
this.prepare_callback = new LocalPrepareCallback(hstore_site);
this.finish_callback = new LocalFinishCallback(hstore_site);
this.rpc_transactionInit = new ProtoRpcController[catalogContext.numberOfSites];
this.rpc_transactionWork = new ProtoRpcController[catalogContext.numberOfSites];
// this.rpc_transactionPrepare = new ProtoRpcController[catalogContext.numberOfSites];
this.rpc_transactionFinish = new ProtoRpcController[catalogContext.numberOfSites];
}
public DistributedState init(LocalTransaction ts) {
this.ts = ts;
// Initialize the prepare callback.
// We have to do this in order to support early 2PC prepares
PartitionSet partitions = ts.getPredictTouchedPartitions();
this.prepare_callback.init(this.ts, partitions);
// Compute whether all of the partitions for this txn are at the same local site
for (int partition : partitions.values()) {
if (ts.hstore_site.isLocalPartition(partition) == false) {
this.is_all_local = false;
break;
}
} // FOR
return (this);
}
@Override
public boolean isInitialized() {
return (this.ts != null);
}
@Override
public void finish() {
this.prepare_callback.finish();
this.finish_callback.finish();
this.is_all_local = true;
this.exec_donePartitions.clear();
this.notified_prepare.clear();
this.notified_finish.set(false);
this.sent_parameters.clear();
for (int i = 0; i < this.rpc_transactionInit.length; i++) {
if (this.rpc_transactionInit[i] != null)
this.rpc_transactionInit[i].reset();
if (this.rpc_transactionWork[i] != null)
this.rpc_transactionWork[i].reset();
// if (this.rpc_transactionPrepare[i] != null)
// this.rpc_transactionPrepare[i].reset();
if (this.rpc_transactionFinish[i] != null)
this.rpc_transactionFinish[i].reset();
} // FOR
this.ts = null;
}
protected ProtoRpcController getTransactionInitController(int site_id) {
return this.getProtoRpcController(this.rpc_transactionInit, site_id);
}
protected ProtoRpcController getTransactionWorkController(int site_id) {
return this.getProtoRpcController(this.rpc_transactionWork, site_id);
}
protected ProtoRpcController getTransactionPrepareController(int site_id) {
// Always create a new ProtoRpcController
return new ProtoRpcController();
// return this.getProtoRpcController(this.rpc_transactionPrepare, site_id);
}
protected ProtoRpcController getTransactionFinishController(int site_id) {
return this.getProtoRpcController(this.rpc_transactionFinish, site_id);
}
private final ProtoRpcController getProtoRpcController(ProtoRpcController cache[], int site_id) {
if (cache[site_id] == null) {
cache[site_id] = new ProtoRpcController();
}
return (cache[site_id]);
}
}