// ----------------------------------------------
// PROCEDURE PARTITIONING PARAMETER
// ----------------------------------------------
} else {
Procedure current_proc = (Procedure) current;
ProcParameter current_proc_param = (ProcParameter) attribute;
assert (current_proc_param != null);
current_proc.setPartitionparameter(current_proc_param.getIndex());
memory = parent.memory;
current_attribute = current_proc_param;
}
// ----------------------------------------------
// COST ESTIMATION
// ----------------------------------------------
Double cost = null;
Double singlep_txns = null;
// Don't estimate the cost if it doesn't fit
if (!memory_exceeded) {
cost = this.cost_model.estimateWorkloadCost(info.catalogContext, info.workload, filter, best_vertex.cost);
singlep_txns = this.cost_model.getSinglePartitionProcedureHistogram().getSampleCount() / (double) this.cost_model.getProcedureHistogram().getSampleCount();
} else {
cost = Double.MAX_VALUE;
}
StateVertex state = new StateVertex(parent, current_key, attribute_key, is_table, cost, singlep_txns, memory);
if (debug.val)
state.debug = cost_model.debugHistograms(info.catalogContext);
// if (!this.graph.containsVertex(parent))
// this.graph.addVertex(parent);
// this.graph.addEdge(new StateEdge(), parent, state,
// EdgeType.DIRECTED);
if (local_best_vertex == null || local_best_vertex.cost > state.cost)
local_best_vertex = state;
assert (local_best_vertex != null);
// ----------------------------------------------
// DEBUG OUTPUT
// ----------------------------------------------
// state.debug = this.cost_model.getLastDebugMessage();
LOG.info(this.createLevelOutput(state, CatalogUtil.getDisplayName(current_attribute, false), spacer, memory_exceeded));
// ----------------------------------------------
// ANALYSIS
// ----------------------------------------------
if (memory_exceeded) {
if (debug.val)
LOG.debug("Memory exceeeded! Skipping solution!");
if (vp_col != null) {
this.revertVerticalPartitionColumn(vp_col);
vp_col = null;
}
continue;
}
// The cost of our parent can never be greater than our cost
if (!parent.isStartVertex()) {
boolean is_valid = MathUtil.greaterThanEquals(cost.floatValue(), parent.cost.floatValue(), 0.001f);
if (!is_valid) { // && debug.get()) {
Map<String, Object> m0 = new LinkedHashMap<String, Object>();
m0.put("Parent State", parent.toString());
m0.put("Parent CostModel", parent.debug);
m0.put("Parent Catalog", createPartitionPlan(hints, parent, false, false));
Map<String, Object> m1 = new LinkedHashMap<String, Object>();
m1.put("Current Attribute", current_attribute.fullName());
m1.put("Current State", state.toString());
m1.put("Current CostModel", cost_model.debugHistograms(info.catalogContext));
m1.put("Current Catalog", createPartitionPlan(hints, state, false, false));
LOG.error("CURRENT COST IS GREATER THAN CURRENT COST! THIS CANNOT HAPPEN!\n" + StringUtil.formatMaps(m0, m1));
cost_model.clear();
double cost2 = this.cost_model.estimateWorkloadCost(info.catalogContext, info.workload, filter, best_vertex.cost);
LOG.error("Second Time: " + cost2);
}
assert (is_valid) : attribute_key + ": Parent[" + parent.cost + "] <= Current[" + cost + "]" + "\n" + "Rounded: Parent[" + MathUtil.roundToDecimals(parent.cost, 2)
+ "] <= Current[" + MathUtil.roundToDecimals(cost, 2) + "]";
}
// If this is a complete solution, then check whether if it is
// the best one that we have seen thus far
// Allow the current solution to be the best solution if the
// following criteria are met:
// (1) It's a complete solution
// (2) It has not exhausted our per-partition memory limit
// (4) It is less than the upper bounds limit
// (3) And one of the following is true:
// (a) The current best solution is the start vertex
// (b) Or the current solution has a cost less than the current
// best solution
if (complete_solution && memory_exceeded == false && cost < BranchAndBoundPartitioner.this.upper_bounds_vertex.cost
&& (BranchAndBoundPartitioner.this.best_vertex.isStartVertex() || cost < BranchAndBoundPartitioner.this.best_vertex.cost)) {
assert (best_vertex.cost > state.cost) : "Best=" + best_vertex.cost + ", Current=" + state.cost;
assert (upper_bounds_vertex.cost > state.cost) : "Upper=" + upper_bounds_vertex.cost + ", Current=" + state.cost;
if (debug.val) {
LOG.debug("Old Solution:\n" + StringBoxUtil.box(best_vertex.toString()));
}
BranchAndBoundPartitioner.this.best_vertex = state;
if (debug.val) {
LOG.debug("New Best Solution:\n" + StringBoxUtil.box(best_vertex.toString()));
if (this.cost_model.hasDebugMessages())
LOG.debug("Last Cost Model Info:\n " + this.cost_model.getLastDebugMessage());
}
// Log new solution cost
if (hints.shouldLogSolutionCosts())
hints.logSolutionCost(state.cost, state.singlep_txns);
// Check whether we found our target solution and need to
// stop
// Note that we only need to compare Tables, because the
// Procedure's could have
// different parameters for those ones where the parameter
// actually doesn't make a difference
if (hints.target_plan != null) {
if (debug.val)
LOG.info("Comparing new best solution with target PartitionPlan");
PartitionPlan new_plan = createPartitionPlan(hints, best_vertex, false, false);
if (hints.target_plan.getTableEntries().equals(new_plan.getTableEntries())) {
this.halt_search = true;
this.halt_reason = HaltReason.FOUND_TARGET;
}
}
// for (int i = 0; i <
// ((TimeIntervalCostModel)this.cost_model).getIntevalCount();
// i++) {
// System.err.println("Interval #" + i);
// System.err.println(((TimeIntervalCostModel)this.cost_model).getCostModel(i).getTxnPartitionAccessHistogram());
// System.err.println("================================================");
// }
//
// System.exit(1);
}
// ----------------------------------------------
// CONTINUE SEARCH TRAVERSAL
// ----------------------------------------------
// We now need to look to see whether we should continue down
// this path. Much like
// selecting a new best solution, the following criteria must be
// met in order for
// the partitioner to be allowed to continue down a search path:
// (1) If this is last attribute at this level in the tree
// AND we're looking at a TABLE
// AND we're doing a greedy search
// Then we'll always want to keep traversing here.
// Otherwise, the following the criteria must be met:
// (1) It must not be a complete solution
// (2) The current catalog item must be a table (no procedures!)
// (3) The cost must be less than the current best solution cost
// (4) The cost must be less than the upper bounds limit
// Or we can just say screw all that and keep going if the
// exhaustive flag is enabled
if (this.halt_search == false
&& ((last_attribute && is_table && this.hints.greedy_search) || (this.hints.exhaustive_search == true) || (complete_solution == false && is_table
&& cost < BranchAndBoundPartitioner.this.best_vertex.cost && cost < BranchAndBoundPartitioner.this.upper_bounds_vertex.cost))) {
// IMPORTANT: If this is the last table in our traversal,
// then we need to switch over
// to working Procedures->ProcParameters. We have to now
// calculate the list of attributes
// that we want to consider
// if (is_last_table &&
// this.hints.enable_procparameter_search) {
// for (int i = idx + 1; i < this.num_elements; i++) {
// Procedure catalog_proc =
// (Procedure)this.all_search_elements.get(i);
// LinkedList<String> attributes =
// AbstractPartitioner.generateProcParameterOrder(info,
// this.info.catalogContext.database, catalog_proc, hints);
//
// // We should have ProcParameter candidates!
// assert(attributes.isEmpty() == false) :
// "No ProcParameter candidates: " + catalog_proc + "\n" +
// state;
// this.traversal_attributes.get(proc_key).clear();
// this.traversal_attributes.get(proc_key).addAll(attributes);
// } // FOR
// }
// We only traverse if this is a table. The ProcParameter
// selection is a simple greedy algorithm
if (this.hints.greedy_search == false || (this.hints.greedy_search == true && last_attribute)) {
if (debug.val && this.hints.greedy_search)
LOG.debug(this.createLevelOutput(local_best_vertex, "GREEDY->" + local_best_vertex.getPartitionKey(), spacer, false));
this.cp.update(current);
this.traverse((this.hints.greedy_search ? local_best_vertex : state), idx + 1);
this.cp.reset(current);
}
}
if (vp_col != null) {
this.revertVerticalPartitionColumn(vp_col);
vp_col = null;
}
if (this.halt_search)
break;
} // FOR
// ProcParameter Traversal
if (!is_table && !this.halt_search) {
assert (local_best_vertex != null) : "Missing local best vertex for " + current_key;
// Set the partitioning ProcParameter in this Procedure to be
// the one that
// had the lowest cost in our search up above.
Procedure current_proc = (Procedure) current;
String best_key = local_best_vertex.getPartitionKey();
ProcParameter current_param = CatalogKey.getFromKey(info.catalogContext.database, best_key, ProcParameter.class);
assert (current_param != null);
current_proc.setPartitionparameter(current_param.getIndex());
// Is this necessary?
this.cost_model.invalidateCache(current_proc);
// if (debug.val)
// LOG.debug(this.createLevelOutput(local_best_vertex,
// CatalogUtil.getDisplayName(current_param), spacer, false));
// If there are more Procedures to partition and we have gone
// past our best cost
// our upper bounds, then keep going...
if (complete_solution == false && hints.enable_procparameter_search && (this.hints.greedy_search == true)
|| (local_best_vertex.cost < best_vertex.cost && local_best_vertex.cost < upper_bounds_vertex.cost)) {
this.cp.update(current_proc);
this.traverse(local_best_vertex, idx + 1);
this.cp.reset(current_proc);
}
// Important: We will have to unset the Procedure partitioning
// parameter
// if this was the last table and we are going back up the tree.
// This is because
// unlike Tables, where we exclude queries using
// WorkloadFilters, we can't do
// the same thing for ProcParameters
// manually once we finish up with this table because the next
// passes will
// overwrite the catalog that we saved off
for (int i = idx; i < this.num_elements; i++) {
Procedure catalog_proc = (Procedure) this.all_search_elements.get(i);
catalog_proc.setPartitionparameter(-1);
} // FOR
}
if (this.halt_search == false)
this.backtrack_ctr++;
return;