package edu.brown.hstore.estimators;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.voltdb.CatalogContext;
import org.voltdb.catalog.Statement;
import org.voltdb.utils.EstTime;
import edu.brown.catalog.special.CountedStatement;
import edu.brown.pools.Poolable;
import edu.brown.utils.CollectionUtil;
import edu.brown.utils.PartitionSet;
import edu.brown.utils.StringUtil;
public abstract class EstimatorState implements Poolable {
protected final CatalogContext catalogContext;
protected final PartitionSet touched_partitions = new PartitionSet();
protected final Map<Statement, Integer> query_instance_cnts = new HashMap<Statement, Integer>();
protected final List<CountedStatement> prefetchable_stmts = new ArrayList<CountedStatement>();
protected Long txn_id = null;
protected int base_partition;
protected long start_time;
private Estimate initialEstimate;
private final List<Estimate> estimates = new ArrayList<Estimate>();
private boolean shouldAllowUpdates = false;
private boolean allowUpdates = true;
/**
* Constructor
* @param markov - the graph that this txn is using
* @param estimated_path - the initial path estimation from MarkovPathEstimator
*/
protected EstimatorState(CatalogContext catalogContext) {
this.catalogContext = catalogContext;
}
public void init(Long txn_id, int base_partition, long start_time) {
this.txn_id = txn_id;
this.base_partition = base_partition;
this.start_time = start_time;
}
@Override
public boolean isInitialized() {
return (this.txn_id != null);
}
@Override
public void finish() {
this.initialEstimate = null;
this.shouldAllowUpdates = false;
this.allowUpdates = true;
for (Estimate estimate : this.estimates) {
if (estimate != null) estimate.finish();
} // FOR
this.estimates.clear();
this.touched_partitions.clear();
this.query_instance_cnts.clear();
this.prefetchable_stmts.clear();
this.txn_id = null;
}
public final Long getTransactionId() {
return (this.txn_id);
}
public final int getBasePartition() {
return (this.base_partition);
}
public final long getStartTime() {
return (this.start_time);
}
public PartitionSet getTouchedPartitions() {
return (this.touched_partitions);
}
public List<CountedStatement> getPrefetchableStatements() {
return (this.prefetchable_stmts);
}
// ----------------------------------------------------------------------------
// TRANSACTION ESTIMATES
// ----------------------------------------------------------------------------
/**
* Returns true if the TransactionEstimator thinks that the PartitionExecutor
* should provide it with updates about txns.
* @return
*/
public final boolean shouldAllowUpdates() {
return (this.shouldAllowUpdates);
}
protected void shouldAllowUpdates(boolean enable) {
this.shouldAllowUpdates = enable;
}
public final void disableUpdates() {
this.allowUpdates = false;
}
public final void enableUpdates() {
this.allowUpdates = true;
}
public final boolean isUpdatesEnabled() {
return (this.allowUpdates);
}
protected void addInitialEstimate(Estimate estimate) {
assert(this.initialEstimate == null);
this.initialEstimate = estimate;
}
protected Estimate addEstimate(Estimate est) {
// assert(this.initialEstimate != null) :
// "Trying to add a new estimate before the initial estimate";
assert(est.isInitialized());
this.estimates.add(est);
return (est);
}
/**
* Get the number of TransactionEstimates generated for this transaction
* This count does <B>not</B> include the initial estimate
* @return
*/
public int getEstimateCount() {
return (this.estimates.size());
}
/**
* Return the full list of estimates generated for this transaction
* <B>NOTE:</B> This should only be used for testing
* @return
*/
public List<Estimate> getEstimates() {
return (this.estimates);
}
/**
* Return the initial TransactionEstimate made for this transaction
* before it began execution
* @return
*/
@SuppressWarnings("unchecked")
public final <T extends Estimate> T getInitialEstimate() {
return ((T)this.initialEstimate);
}
/**
* Return the last TransactionEstimate made for this transaction
* If no new estimate has been made, then it should return the
* initial estimate
* @return
*/
@SuppressWarnings("unchecked")
public final <T extends Estimate> T getLastEstimate() {
if (this.estimates.isEmpty()) {
return (T)this.initialEstimate;
}
return ((T)CollectionUtil.last(this.estimates));
}
// ----------------------------------------------------------------------------
// API METHODS
// ----------------------------------------------------------------------------
/**
* Get the number of milli-seconds that have passed since the txn started
* @return
*/
public long getExecutionTimeOffset() {
return (EstTime.currentTimeMillis() - this.start_time);
}
public long getExecutionTimeOffset(long stop) {
return (stop - this.start_time);
}
public int updateQueryInstanceCount(Statement catalog_stmt) {
Integer cnt = this.query_instance_cnts.get(catalog_stmt);
if (cnt == null) cnt = 0;
this.query_instance_cnts.put(catalog_stmt, cnt.intValue() + 1);
return (cnt.intValue());
}
@Override
public String toString() {
Map<String, Object> m0 = new LinkedHashMap<String, Object>();
m0.put("TransactionId", this.txn_id);
m0.put("Base Partition", this.base_partition);
m0.put("Touched Partitions", this.touched_partitions);
m0.put("Start Time", this.start_time);
m0.put("Prefetchable Statements", this.prefetchable_stmts);
return StringUtil.formatMaps(m0);
}
public void addPrefetchableStatement(CountedStatement cntStmt) {
this.prefetchable_stmts.add(cntStmt);
}
}