if (debug.val)
LOG.debug(String.format("%s - Estimating execution path (%s)",
TransactionUtil.formatTxnName(catalog_proc, state.getTransactionId()),
(est.isInitialEstimate() ? "INITIAL" : "BATCH #" + est.getBatchId())));
MarkovVertex currentVertex = est.getVertex();
assert(currentVertex != null);
if (this.enable_recomputes) {
this.markovTimes.addInstanceTime(currentVertex, state.getTransactionId(), EstTime.currentTimeMillis());
}
// TODO: If the current vertex is in the initial estimate's list,
// then we can just use the truncated list as the estimate, since we know
// that the path will be the same. We don't need to recalculate everything
MarkovGraph markov = state.getMarkovGraph();
assert(markov != null) :
String.format("Unexpected null MarkovGraph for %s [hashCode=%d]\n%s",
TransactionUtil.formatTxnName(catalog_proc, state.getTransactionId()),
state.hashCode(), state);
boolean compute_path = true;
if (hstore_conf.site.markov_fast_path && currentVertex.isStartVertex() == false) {
List<MarkovVertex> initialPath = ((MarkovEstimate)state.getInitialEstimate()).getMarkovPath();
if (initialPath.contains(currentVertex)) {
if (debug.val)
LOG.debug(String.format("%s - Using fast path estimation for %s[#%d]",
TransactionUtil.formatTxnName(catalog_proc, state.getTransactionId()), markov, markov.getGraphId()));
if (this.profiler != null) timestamp = ProfileMeasurement.getTime();
try {
MarkovPathEstimator.fastEstimation(est, initialPath, currentVertex);
compute_path = false;
} finally {
if (this.profiler != null) this.profiler.fastest_time.appendTime(timestamp);
}
}
}
// We'll reuse the last MarkovPathEstimator (and it's path) if the graph has been accurate for
// other previous transactions. This prevents us from having to recompute the path every single time,
// especially for single-partition transactions where the clustered MarkovGraphs are accurate
else if (hstore_conf.site.markov_path_caching) {
List<MarkovVertex> cached = this.cached_paths.get(markov);
if (cached == null) {
if (debug.val)
LOG.debug(String.format("%s - No cached path available for %s[#%d]",
TransactionUtil.formatTxnName(catalog_proc, state.getTransactionId()),
markov, markov.getGraphId()));
}
else if (markov.getAccuracyRatio() < hstore_conf.site.markov_path_caching_threshold) {
if (debug.val)
LOG.debug(String.format("%s - MarkovGraph %s[#%d] accuracy is below caching threshold [%.02f < %.02f]",
TransactionUtil.formatTxnName(catalog_proc, state.getTransactionId()),
markov, markov.getGraphId(), markov.getAccuracyRatio(),
hstore_conf.site.markov_path_caching_threshold));
}
else {
if (debug.val)
LOG.debug(String.format("%s - Using cached path for %s[#%d]",
TransactionUtil.formatTxnName(catalog_proc, state.getTransactionId()),
markov, markov.getGraphId()));
if (this.profiler != null) timestamp = ProfileMeasurement.getTime();
try {
MarkovPathEstimator.fastEstimation(est, cached, currentVertex);
compute_path = false;
} finally {
if (this.profiler != null) this.profiler.cachedest_time.appendTime(timestamp);
}
}
}
// Use the MarkovPathEstimator to estimate a new path for this txn
if (compute_path) {
if (debug.val)
LOG.debug(String.format("%s - Need to compute new path in %s[#%d] using %s",
TransactionUtil.formatTxnName(catalog_proc, state.getTransactionId()),
markov, markov.getGraphId(),
MarkovPathEstimator.class.getSimpleName()));
MarkovPathEstimator pathEstimator = null;
try {
pathEstimator = (MarkovPathEstimator)this.pathEstimatorsPool.borrowObject();
pathEstimator.init(state.getMarkovGraph(), est, args, state.getBasePartition());
pathEstimator.setForceTraversal(hstore_conf.site.markov_force_traversal);
pathEstimator.setLearningEnabled(hstore_conf.site.markov_learning_enable);
} catch (Throwable ex) {
String txnName = TransactionUtil.formatTxnName(catalog_proc, state.getTransactionId());
String msg = "Failed to intitialize new MarkovPathEstimator for " + txnName;
LOG.error(msg, ex);
throw new RuntimeException(msg, ex);
}
if (this.profiler != null) timestamp = ProfileMeasurement.getTime();
try {
pathEstimator.traverse(est.getVertex());
} catch (Throwable ex) {
try {
GraphvizExport<MarkovVertex, MarkovEdge> gv = MarkovUtil.exportGraphviz(markov, true, markov.getPath(pathEstimator.getVisitPath()));
LOG.error("GRAPH #" + markov.getGraphId() + " DUMP: " + gv.writeToTempFile(catalog_proc));
} catch (Exception ex2) {
throw new RuntimeException(ex2);
}
String msg = "Failed to estimate path for " + TransactionUtil.formatTxnName(catalog_proc, state.getTransactionId());
LOG.error(msg, ex);
throw new RuntimeException(msg, ex);
} finally {
if (this.profiler != null) this.profiler.fullest_time.appendTime(timestamp);
}
// If our path was incomplete or we created new vertices during the traversal,
// then we should tell the PartitionExecutor that we need updates about this
// txn so that we can populate the MarkovGraph
if (hstore_conf.site.markov_learning_enable && est.isInitialEstimate()) {
Collection<MarkovVertex> createdVertices = pathEstimator.getCreatedVertices();
MarkovVertex v = CollectionUtil.last(est.getMarkovPath());
if ((createdVertices != null && createdVertices.isEmpty() == false) ||
(v.isQueryVertex() == true || v.isStartVertex())) {
if (debug.val)
LOG.debug(String.format("Enabling runtime updates for %s " +
"[createdVertices=%s, lastVertex=%s]",
state.getTransactionId(), createdVertices, v));
state.shouldAllowUpdates(true);