package edu.brown.markov;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections15.map.ListOrderedMap;
import org.apache.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONStringer;
import org.voltdb.catalog.CatalogType;
import org.voltdb.catalog.Database;
import org.voltdb.catalog.Statement;
import org.voltdb.utils.NotImplementedException;
import edu.brown.catalog.CatalogKey;
import edu.brown.catalog.CatalogUtil;
import edu.brown.catalog.special.CountedStatement;
import edu.brown.graphs.AbstractVertex;
import edu.brown.graphs.exceptions.InvalidGraphElementException;
import edu.brown.hstore.estimators.DynamicTransactionEstimate;
import edu.brown.hstore.estimators.EstimatorUtil;
import edu.brown.logging.LoggerUtil;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
import edu.brown.utils.ClassUtil;
import edu.brown.utils.CollectionUtil;
import edu.brown.utils.MathUtil;
import edu.brown.utils.PartitionSet;
import edu.brown.utils.StringUtil;
import edu.brown.utils.TableUtil;
/**
* Markov Model Vertex
* @author svelagap
* @author pavlo
*/
public class MarkovVertex extends AbstractVertex implements MarkovHitTrackable, DynamicTransactionEstimate {
private static final Logger LOG = Logger.getLogger(MarkovVertex.class);
private final static LoggerBoolean debug = new LoggerBoolean();
private final static LoggerBoolean trace = new LoggerBoolean();
static {
LoggerUtil.attachObserver(LOG, debug, trace);
}
/**
* This is the partition id that is used for probabilities that are not partition specific
* For example, the ABORT probability is global to all partitions, so we only need to store one
* value for it
*/
private static final int DEFAULT_PARTITION_ID = 0;
// ----------------------------------------------------------------------------
// INTERNAL DATA ENUMS
// ----------------------------------------------------------------------------
public enum Members {
COUNTER,
TYPE,
PARTITIONS,
PAST_PARTITIONS,
TOTALHITS,
INSTANCEHITS,
PROBABILITIES,
EXECUTION_TIME,
};
public enum Type {
QUERY,
START,
COMMIT,
ABORT
};
public enum Probability {
// SINGLE_SITED (true, 0.0f),
ABORT (true, 0.0f),
// READ_ONLY (false, 0.0f),
WRITE (false, 0.0f),
DONE (false, 1.0f);
final boolean single_value;
final float default_value;
Probability(boolean single_value, float default_value) {
this.single_value = single_value;
this.default_value = default_value;
}
protected static final Map<String, Probability> name_lookup = new HashMap<String, Probability>();
static {
for (Probability vt : EnumSet.allOf(Probability.class)) {
Probability.name_lookup.put(vt.name().toLowerCase(), vt);
}
}
public static Probability get(int idx) {
assert(idx >= 0);
return (Probability.values()[idx]);
}
public static Probability get(String name) {
return (name_lookup.get(name.toLowerCase()));
}
};
// ----------------------------------------------------------------------------
// GLOBAL CONFIGURATION
// ----------------------------------------------------------------------------
/**
* The StmtCounter is the number of times that this particular Statement
* was executed previously in the current transaction.
*/
public int counter;
/**
* The number of times this Vertex has been traversed
*/
public int totalhits = 0;
/**
* The type of this vertex: Abort/Stop/Query/Start
*/
public Type type;
/**
* The partitions this query touches
*/
public final PartitionSet partitions = new PartitionSet();
/**
* The partitions that the txn has touched in the past
*/
public PartitionSet past_partitions = new PartitionSet();
// ----------------------------------------------------------------------------
// ADDITIONAL NON-STATE DATA MEMBERS
// ----------------------------------------------------------------------------
/**
* The average execution time of this transaction
*/
public long execution_time = 0l;
/**
* Mapping from Probability type to another map from partition id
*/
public float probabilities[][];
// ----------------------------------------------------------------------------
// TRANSIENT DATA MEMBERS
// ----------------------------------------------------------------------------
/**
* The number of times this vertex has been touched in the current on-line run
*/
public transient int instancehits = 0;
/**
* The count, used to figure out the average execution time above
*/
private transient long execution_time_count = 0l;
/**
* Cached output for toString()
* This is actually used for faster .equals() lookups too
*/
private transient String to_string = null;
/**
* Special wrapper object that contains the Statement + the query counter
*/
private transient CountedStatement counted_stmt = null;
// ----------------------------------------------------------------------------
// CONSTRUCTORS
// ----------------------------------------------------------------------------
/**
* Empty constructor
*/
public MarkovVertex() {
// This is needed for serialization
super();
this.probabilities = new float[MarkovVertex.Probability.values().length][];
}
/**
* Constructor for barebones vertices such as STOP, ABORT, and START
* @param catalog_stmt
* @param type
*/
public MarkovVertex(Statement catalog_stmt, MarkovVertex.Type type) {
this(catalog_stmt, type, 0, null, null);
}
/**
* Constructor used to create the actual graphs
* @param catalog_stmt - query this vertex is associated with
* @param type - QUERY, ABORT, START, or STOP
* @param query_instance_index - the number of times we've executed this query before
* @param partitions - the partitions this procedure touches
* @param past_partitions - the partitions that we've touched in the past
*/
public MarkovVertex(Statement catalog_stmt, MarkovVertex.Type type, int query_instance_index, PartitionSet partitions, PartitionSet past_partitions) {
super(catalog_stmt);
this.type = type;
if (partitions != null) this.partitions.addAll(partitions);
if (past_partitions != null) this.past_partitions.addAll(past_partitions);
this.counter = query_instance_index;
this.probabilities = new float[MarkovVertex.Probability.values().length][];
this.init();
}
/**
* Copy Constructor
* Only really used for testing
* @param v
*/
public MarkovVertex(MarkovVertex v) {
super(v.getCatalogItem());
this.type = v.type;
this.partitions.addAll(v.partitions);
this.past_partitions.addAll(v.past_partitions);
this.counter = v.counter;
this.probabilities = new float[MarkovVertex.Probability.values().length][];
this.init();
for (int i = 0; i < v.probabilities.length; i++) {
for (int j = 0; j < v.probabilities[i].length; j++) {
this.probabilities[i][j] = v.probabilities[i][j];
} // FOR
} // FOR
}
/**
* Initialize the probability tables
*/
private void init() {
@SuppressWarnings("deprecation")
int num_partitions = CatalogUtil.getNumberOfPartitions(this.catalog_item);
for (MarkovVertex.Probability ptype : MarkovVertex.Probability.values()) {
int inner_len = (ptype.single_value ? 1 : num_partitions);
this.probabilities[ptype.ordinal()] = new float[inner_len];
} // FOR
this.resetAllProbabilities();
}
@Override
public boolean isInitialized() {
return (true);
}
@Override
public void finish() {
// Nothing to do
}
@Override
public boolean hasQueryEstimate(int partition) {
return false;
}
@Override
public List<CountedStatement> getQueryEstimate(int partition) {
throw new NotImplementedException(ClassUtil.getCurrentMethodName() + " is not implemented");
}
@Override
public int getBatchId() {
return (EstimatorUtil.INITIAL_ESTIMATE_BATCH);
}
@Override
public boolean isInitialEstimate() {
return (true);
}
@Override
public boolean isValid() {
return (true);
}
public boolean isValid(MarkovGraph markov) {
try {
this.validate(markov);
} catch (InvalidGraphElementException ex) {
return (false);
}
return (true);
}
protected void validate(MarkovGraph markov) throws InvalidGraphElementException {
Collection<MarkovEdge> outbound = markov.getOutEdges(this);
Collection<MarkovEdge> inbound = markov.getInEdges(this);
switch (this.type) {
case START: {
// START should not have any inbound edges
if (inbound.size() > 0) {
String msg = String.format("START state has %d inbound edges", outbound.size());
throw new InvalidGraphElementException(markov, this, msg);
}
break;
}
case COMMIT:
case ABORT: {
// COMMIT and ABORT should not have any outbound edges
if (outbound.size() > 0) {
String msg = String.format("%s state has %d outbound edges", this.type, outbound.size());
throw new InvalidGraphElementException(markov, this, msg);
}
break;
}
case QUERY: {
// Every QUERY vertex should have an inbound edge
if (inbound.isEmpty()) {
throw new InvalidGraphElementException(markov, this, "QUERY state does not have any inbound edges");
}
for (MarkovVertex.Probability ptype : MarkovVertex.Probability.values()) {
int idx = ptype.ordinal();
for (int i = 0, cnt = this.probabilities[idx].length; i < cnt; i++) {
float prob = this.probabilities[idx][i];
if (MathUtil.greaterThanEquals(prob, 0.0f, MarkovGraph.PROBABILITY_EPSILON) == false ||
MathUtil.lessThanEquals(prob, 1.0f, MarkovGraph.PROBABILITY_EPSILON) == false) {
String msg = String.format("Invalid %s probability at partition #%d: %f", ptype.name(), i, prob);
throw new InvalidGraphElementException(markov, this, msg);
}
} // FOR
}
// If this isn't the first time we are executing this query, then we should at least have
// past partitions that we have touched
if (this.counter > 0 && this.past_partitions.isEmpty()) {
String msg = "No past partitions for at non-first query vertex";
throw new InvalidGraphElementException(markov, this, msg);
}
// And we should always have some partitions that we're touching now
if (this.partitions.isEmpty()) {
String msg = "No current partitions";
throw new InvalidGraphElementException(markov, this, msg);
}
break;
}
default:
assert(false) : "Unexpected vertex type " + this.type;
} // SWITCH
}
// ----------------------------------------------------------------------------
// DATA MEMBER METHODS
// ----------------------------------------------------------------------------
/**
* Return the vertex type
* @return
*/
public Type getType() {
return this.type;
}
/**
* Returns true if this Vertex is one of the ending states (commit/abort)
* @return
*/
public boolean isEndingVertex() {
return (this.type == Type.COMMIT || this.type == Type.ABORT);
}
public boolean isQueryVertex() {
return (this.type == Type.QUERY);
}
public boolean isStartVertex() {
return (this.type == Type.START);
}
public boolean isCommitVertex() {
return (this.type == Type.COMMIT);
}
public boolean isAbortVertex() {
return (this.type == Type.ABORT);
}
/**
* The number of times that the txn has executed this query in the past.
* Offset starts at zero.
* @return
*/
public int getQueryCounter() {
return (int)this.counter;
}
public CountedStatement getCountedStatement() {
if (this.counted_stmt == null) {
synchronized (this) {
if (this.counted_stmt == null) {
this.counted_stmt = new CountedStatement((Statement)this.catalog_item, this.counter);
}
} // SYNCH
}
return (this.counted_stmt);
}
/**
* Return the set of partitions that the query represented by this vertex touches
* @return
*/
public PartitionSet getPartitions() {
return this.partitions;
}
/**
* Return the set of partitions that the txn has touched in the past
* @return
*/
public PartitionSet getPastPartitions() {
return this.past_partitions;
}
@Override
public PartitionSet getTouchedPartitions(EstimationThresholds t) {
return (this.partitions);
}
public boolean equals(Object o) {
if (o instanceof MarkovVertex) {
MarkovVertex v = (MarkovVertex) o;
if (this.to_string == null) this.toString();
if (v.to_string == null) v.toString();
return (this.to_string.equals(v.to_string));
// return (this.type.equals(v.type) &&
// this.catalog_item.equals(v.catalog_item) &&
// this.partitions.equals(v.partitions) &&
// (MarkovGraph.USE_PAST_PARTITIONS == false || this.past_partitions.equals(v.past_partitions)) &&
// this.query_instance_index == v.query_instance_index);
}
return false;
}
/**
* Perform equality check distinct from equals() method. Checks partitions, catalog_statement,
* and the index of the query within the transaction
* @param other_stmt
* @param other_partitions
* @param other_past
* @param other_queryInstanceIndex
* @return
*/
public boolean isEqual(Statement other_stmt, PartitionSet other_partitions, PartitionSet other_past, int other_queryInstanceIndex) {
return (this.isEqual(other_stmt, other_partitions, other_past, other_queryInstanceIndex, MarkovGraph.USE_PAST_PARTITIONS));
}
/**
* Perform equality check distinct from equals() method. Checks partitions, catalog_statement,
* and the index of the query within the transaction
* This version of isEqual() allows you to pass in the use_past_partitions flag
*
* @param other_stmt
* @param other_partitions
* @param other_past
* @param other_queryInstanceIndex
* @param use_past_partitions
* @return
*/
public boolean isEqual(Statement other_stmt, PartitionSet other_partitions, PartitionSet other_past, int other_queryInstanceIndex, boolean use_past_partitions) {
return (other_queryInstanceIndex == this.counter &&
other_stmt.equals(this.catalog_item) &&
this.partitions.equals(other_partitions) &&
(use_past_partitions ? this.past_partitions.equals(other_past) : true));
}
// ----------------------------------------------------------------------------
// PROBABILITY METHODS
// ----------------------------------------------------------------------------
/**
* Returns true if the query for this vertex only touches one partition and that
* partition is the as the base_partition (i.e., where the procedure's Java code is executing)
* @return
*/
public boolean isLocalPartitionOnly() {
if (this.type == Type.QUERY) {
// If there is more than one partition then we know immediately that this is busted
if (this.partitions.size() != 1) return (false);
// If there are not past partitions yet, then yes this is technically single-partitioned
if (this.past_partitions.isEmpty()) return (true);
// Lastly, we can check...
return (this.partitions.size() == 1 &&
this.past_partitions.size() == 1 &&
this.partitions.containsAll(this.past_partitions));
}
return (true);
}
/**
* Returns the probability of name if it is found in the mapping, otherwise returns d
* @param name
* @param default_value
* @return
*/
private float getSpecificProbability(MarkovVertex.Probability ptype, int partition) {
assert(ptype.ordinal() < this.probabilities.length) : "Unexpected " + ptype.name();
assert(partition < this.probabilities[ptype.ordinal()].length) :
String.format("Invalid partition %d for %s [max=%d]",
partition, ptype, this.probabilities[ptype.ordinal()].length);
float value = this.probabilities[ptype.ordinal()][partition];
if (value == EstimatorUtil.NULL_MARKER) value = ptype.default_value;
return (value);
}
/**
* Use for incrementing a certain probability
* @param name
* @param probability
*/
private void addToProbability(MarkovVertex.Probability ptype, int partition, float probability) {
// Important: If the probability is unset, then we need to set its initial value
// to zero and to the default value
float previous = this.probabilities[ptype.ordinal()][partition];
if (previous == EstimatorUtil.NULL_MARKER) previous = 0.0f;
this.setProbability(ptype, partition, previous + probability);
}
/**
*
* @param ptype
* @param partition
* @param probability
*/
private void setProbability(MarkovVertex.Probability ptype, int partition, float probability) {
if (trace.val)
LOG.trace(String.format("%s :: SET %s%s -> %.3f",
this, ptype,
(ptype.single_value ? "" : "(partition="+partition+")"),
probability));
assert(MathUtil.greaterThanEquals(probability, 0.0f, MarkovGraph.PROBABILITY_EPSILON) &&
MathUtil.lessThanEquals(probability, 1.0f, MarkovGraph.PROBABILITY_EPSILON)) :
String.format("%s :: Invalid %s probability at partition #%d: %f",
this, ptype, partition, probability);
this.probabilities[ptype.ordinal()][partition] = probability;
}
/**
* Reset all probabilities. Keeps partitions in maps
*/
public void resetAllProbabilities() {
for (MarkovVertex.Probability ptype : MarkovVertex.Probability.values()) {
int i = ptype.ordinal();
if (this.probabilities[i] == null) continue;
for (int j = 0; j < this.probabilities[i].length; j++) {
this.probabilities[i][j] = EstimatorUtil.NULL_MARKER;
} // FOR
} // FOR
}
// ----------------------------------------------------------------------------
// SINGLE-SITED PROBABILITY
// ----------------------------------------------------------------------------
// @Override
// public void addSinglePartitionProbability(float probability) {
// this.addToProbability(Probability.SINGLE_SITED, DEFAULT_PARTITION_ID, probability);
// }
// @Override
// public void setSinglePartitionProbability(float probability) {
// this.setProbability(Probability.SINGLE_SITED, DEFAULT_PARTITION_ID, probability);
// }
// @Override
// public float getSinglePartitionProbability() {
// return (this.getSpecificProbability(Probability.SINGLE_SITED, DEFAULT_PARTITION_ID));
// }
// @Override
// public boolean isSinglePartitionProbabilitySet() {
// return (this.getSpecificProbability(Probability.SINGLE_SITED, DEFAULT_PARTITION_ID) != EstimatorUtil.NULL_MARKER);
// }
@Override
public boolean isSinglePartitioned(EstimationThresholds t) {
return (this.getDonePartitions(t).size() == 1);
}
// ----------------------------------------------------------------------------
// READ-ONLY PROBABILITY
// ----------------------------------------------------------------------------
public boolean isReadOnly() {
return ((Statement) this.catalog_item).getReadonly();
}
// @Override
// public void addReadOnlyProbability(int partition, float probability) {
// this.addToProbability(Probability.READ_ONLY, partition, probability);
// }
// @Override
// public void setReadOnlyProbability(int partition, float probability) {
// this.setProbability(Probability.READ_ONLY, partition, probability);
// }
// @Override
// public float getReadOnlyProbability(int partition) {
// return (this.getSpecificProbability(Probability.READ_ONLY, partition));
// }
// @Override
// public boolean isReadOnlyProbabilitySet(int partition) {
// return (this.getSpecificProbability(Probability.READ_ONLY, partition) != EstimatorUtil.NULL_MARKER);
// }
@Override
public boolean isReadOnlyPartition(EstimationThresholds t, int partition) {
return (this.getSpecificProbability(Probability.WRITE, partition) >= t.write);
}
@Override
public boolean isReadOnlyAllPartitions(EstimationThresholds t) {
boolean readonly = true;
for (int p = 0, cnt = this.probabilities[Probability.WRITE.ordinal()].length; p < cnt; p++) {
if (this.getSpecificProbability(Probability.WRITE, p) >= t.write) {
readonly = false;
break;
}
} // FOR
return (readonly);
}
// @Override
// public PartitionSet getReadOnlyPartitions(EstimationThresholds t) {
// PartitionSet partitions = new PartitionSet();
// for (int p = 0, cnt = this.probabilities[Probability.WRITE.ordinal()].length; p < cnt; p++) {
// if (this.isReadOnlyPartition(t, p)) {
// partitions.add(p);
// }
// } // FOR
// return (partitions);
// }
// ----------------------------------------------------------------------------
// WRITE PROBABILITY
// ----------------------------------------------------------------------------
public void addWriteProbability(int partition, float probability) {
this.addToProbability(Probability.WRITE, partition, probability);
}
public void setWriteProbability(int partition, float probability) {
this.setProbability(Probability.WRITE, partition, probability);
}
public float getWriteProbability(int partition) {
return (this.getSpecificProbability(Probability.WRITE, partition));
}
public boolean isWriteProbabilitySet(int partition) {
return (this.getSpecificProbability(Probability.WRITE, partition) != EstimatorUtil.NULL_MARKER);
}
@Override
public boolean isWritePartition(EstimationThresholds t, int partition) {
return (this.getSpecificProbability(Probability.WRITE, partition) >= t.write);
}
@Override
public PartitionSet getWritePartitions(EstimationThresholds t) {
PartitionSet partitions = new PartitionSet();
for (int p = 0, cnt = this.probabilities[Probability.WRITE.ordinal()].length; p < cnt; p++) {
if (this.isWritePartition(t, p)) {
partitions.add(p);
}
} // FOR
return (partitions);
}
// ----------------------------------------------------------------------------
// DONE PROBABILITY
// ----------------------------------------------------------------------------
public void addDoneProbability(int partition, float probability) {
this.addToProbability(Probability.DONE, partition, probability);
}
public void setDoneProbability(int partition, float probability) {
this.setProbability(Probability.DONE, partition, probability);
}
public float getDoneProbability(int partition) {
return (this.getSpecificProbability(Probability.DONE, partition));
}
public boolean isDoneProbabilitySet(int partition) {
return (this.getSpecificProbability(Probability.DONE, partition) != EstimatorUtil.NULL_MARKER);
}
@Override
public boolean isDonePartition(EstimationThresholds t, int partition) {
return (this.getSpecificProbability(Probability.DONE, partition) >= t.done);
}
@Override
public PartitionSet getDonePartitions(EstimationThresholds t) {
PartitionSet partitions = new PartitionSet();
for (int p = 0, cnt = this.probabilities[Probability.DONE.ordinal()].length; p < cnt; p++) {
if (this.isDonePartition(t, p)) {
partitions.add(p);
}
} // FOR
return (partitions);
}
// ----------------------------------------------------------------------------
// ABORT PROBABILITY
// ----------------------------------------------------------------------------
@Override
public void addAbortProbability(float probability) {
this.addToProbability(Probability.ABORT, DEFAULT_PARTITION_ID, probability);
}
@Override
public void setAbortProbability(float probability) {
this.setProbability(Probability.ABORT, DEFAULT_PARTITION_ID, probability);
}
@Override
public float getAbortProbability() {
return (this.getSpecificProbability(Probability.ABORT, DEFAULT_PARTITION_ID));
}
@Override
public boolean isAbortProbabilitySet() {
return (this.getSpecificProbability(Probability.DONE, DEFAULT_PARTITION_ID) != EstimatorUtil.NULL_MARKER);
}
@Override
public boolean isAbortable(EstimationThresholds t) {
float prob = this.getSpecificProbability(Probability.DONE, DEFAULT_PARTITION_ID);
if (prob != EstimatorUtil.NULL_MARKER) {
return (prob >= t.abort);
}
return (true);
}
/**
* The 'score' of a vertex is a measure of how often it has been hit in the current workload.
* When this value differs enough from getOriginalScore() shoudlRecompute() will return true
* @param xact_count
* @return
*/
private double getChangeScore(int xact_count) {
return (double) (this.instancehits * 1.0 / xact_count);
}
/**
* When the hits this vertex has received in this current run differs from the original hitrate enough,
* it returns true.
* @param xact_count
* @param recomputeTolerance - the threshold at which we should recompute
* @param workload_count - the transaction count of the workload used to make the graph this Vertex is a part of
* @return
*/
public boolean shouldRecompute(int xact_count, double recomputeTolerance, int workload_count) {
double original_score = this.totalhits / (1.0f * workload_count);
return (getChangeScore(xact_count) - original_score) / original_score >= recomputeTolerance;
}
public void setExecutiontime(long executiontime) {
this.execution_time = executiontime;
}
/**
* The amount of execution time remaining until a transaction at this vertex commits
* @return
*/
public long getRemainingExecutionTime() {
return this.execution_time;
}
public void addExecutionTime(long l) {
this.execution_time = (this.execution_time * execution_time_count + l) / ++execution_time_count;
}
public void addToExecutionTime(long l) {
this.execution_time += l;
}
// ----------------------------------------------------------------------------
// ONLINE UPDATE METHODS
// ----------------------------------------------------------------------------
@Override
public void applyInstanceHitsToTotalHits() {
this.totalhits += this.instancehits;
this.instancehits = 0;
}
@Override
public void incrementTotalHits() {
this.totalhits++;
}
@Override
public long getTotalHits() {
return this.totalhits;
}
@Override
public void setInstanceHits(int instancehits) {
this.instancehits = instancehits;
}
@Override
public int getInstanceHits() {
return this.instancehits;
}
@Override
public int incrementInstanceHits() {
return (++this.instancehits);
}
// ----------------------------------------------------------------------------
// SERIALIZATION METHODS
// ----------------------------------------------------------------------------
/**
* Implementation of the toJSONString method for an AbstractVertex
*/
public void toJSONStringImpl(JSONStringer stringer) throws JSONException {
super.toJSONStringImpl(stringer);
Set<Members> members_set = CollectionUtil.getAllExcluding(Members.values(), Members.PROBABILITIES);
Members members[] = new Members[members_set.size()];
members_set.toArray(members);
super.fieldsToJSONString(stringer, MarkovVertex.class, members);
// Probabilities Map
stringer.key(Members.PROBABILITIES.name()).object();
for (Probability type : Probability.values()) {
stringer.key(type.name()).array();
int i = type.ordinal();
for (int j = 0, cnt = this.probabilities[i].length; j < cnt; j++) {
stringer.value(this.probabilities[i][j]);
} // FOR
stringer.endArray();
} // FOR
stringer.endObject();
}
@SuppressWarnings("unchecked")
public void fromJSONObjectImpl(JSONObject object, Database catalog_db) throws JSONException {
// Lists in Java suck. We want to let fieldsFromJSONObject handle all our fields except for TYPE
Set<Members> members_set = CollectionUtil.getAllExcluding(Members.values(),
Members.TYPE,
Members.PROBABILITIES,
Members.PARTITIONS,
Members.PAST_PARTITIONS);
Members members[] = new Members[members_set.size()];
members_set.toArray(members);
super.fieldsFromJSONObject(object, catalog_db, MarkovVertex.class, members);
// HACK
this.partitions.clear();
JSONArray json_arr = object.getJSONArray(Members.PARTITIONS.name());
for (int i = 0, cnt = json_arr.length(); i < cnt; i++) {
this.partitions.add(json_arr.getInt(i));
}
this.past_partitions.clear();
json_arr = object.getJSONArray(Members.PAST_PARTITIONS.name());
for (int i = 0, cnt = json_arr.length(); i < cnt; i++) {
this.past_partitions.add(json_arr.getInt(i));
}
// Probabilities Map
JSONObject json_probabilities = object.getJSONObject(Members.PROBABILITIES.name());
Iterator<String> keys = json_probabilities.keys();
while (keys.hasNext()) {
String key = keys.next();
Probability type = Probability.get(key);
assert(type != null) : "Invalid name '" + key + "'";
int i = type.ordinal();
json_arr = json_probabilities.getJSONArray(key);
this.probabilities[i] = new float[json_arr.length()];
for (int j = 0, cnt = this.probabilities[i].length; j < cnt; j++) {
this.probabilities[i][j] = (float)json_arr.getDouble(j);
} // FOR
} // WHILE
// I'm lazy...
String s = object.getString(Members.TYPE.name());
for (Type e : Type.values()) {
if (e.name().startsWith(s)) {
this.type = e;
break;
}
} // FOR
// We have to call this ourselves because we need to be able to handle
// our special START/STOP/ABORT catalog objects
super.fromJSONObjectImpl(object, catalog_db);
this.fieldsFromJSONObject(object, catalog_db, AbstractVertex.class, AbstractVertex.Members.values());
this.catalog_class = (Class<? extends CatalogType>) ClassUtil.getClass(object.getString(AbstractVertex.Members.CATALOG_CLASS.name()));
assert (this.catalog_class != null);
switch (this.type) {
case START:
case COMMIT:
case ABORT:
this.catalog_item = MarkovUtil.getSpecialStatement(catalog_db, this.type);
break;
default:
this.catalog_item = CatalogKey.getFromKey(catalog_db, this.catalog_key, this.catalog_class);
break;
} // SWITCH
}
// ----------------------------------------------------------------------------
// DEBUG METHODS
// ----------------------------------------------------------------------------
@Override
public String toString() {
if (this.to_string == null) {
StringBuilder sb = new StringBuilder();
sb.append("{").append(this.catalog_item.getName());
if (this.type == Type.QUERY) {
sb.append(String.format(" Id:%d,Cnt:%d,Prtns:%s,Past:%s",
this.getElementId(), this.counter, this.partitions, this.past_partitions));
}
sb.append("}");
this.to_string = sb.toString();
}
return (this.to_string);
}
/**
* Produce a table of all the partitions
*/
public String debug() {
Map<String, Object> m0 = new ListOrderedMap<String, Object>();
Map<String, Object> m1 = new ListOrderedMap<String, Object>();
Map<String, String> m2 = null;
DecimalFormat formatter = new DecimalFormat("0.000");
// Basic Information
m0.put("Statement", this.catalog_item.getName() + (this.isQueryVertex() ? "/#" + this.counter : ""));
m0.put("ElementId", this.getElementId());
m0.put("ExecutionTime", this.getRemainingExecutionTime());
m0.put("Total Hits", this.totalhits);
m0.put("Instance Hits", this.instancehits);
// if (true || this.isQueryVertex()) {
m0.put("Partitions", this.partitions);
m0.put("Previous", this.past_partitions);
// Global Probabilities
List<String> header = new ArrayList<String>();
header.add(" ");
MarkovVertex.Probability ptypes[] = MarkovVertex.Probability.values();
for (MarkovVertex.Probability type : ptypes) {
if (type.single_value) {
float val = this.probabilities[type.ordinal()][DEFAULT_PARTITION_ID];
String val_str = (val == EstimatorUtil.NULL_MARKER ? "<NONE>" : formatter.format(val));
m1.put(type.name(), val_str);
} else {
header.add(type.name());
}
} // FOR
// Partition-based Probabilities
int num_partitions = this.probabilities[MarkovVertex.Probability.WRITE.ordinal()].length;
Object rows[][] = new String[num_partitions][header.size()];
for (int row_idx = 0, cnt = num_partitions; row_idx < cnt; row_idx++) {
int col_idx = 0;
rows[row_idx][col_idx++] = String.format("Partition %02d", row_idx);
for (MarkovVertex.Probability type : ptypes) {
if (type.single_value) continue;
float val = this.probabilities[type.ordinal()][row_idx];
rows[row_idx][col_idx++] = (val == EstimatorUtil.NULL_MARKER ? "<NONE>" : formatter.format(val));
} // FOR
} // FOR
m2 = TableUtil.tableMap(header.toArray(new String[0]), rows);
// }
return (StringUtil.formatMaps(m0, m1, m2));
}
}