// We take multiple passes through the partition trees until we come up
// with one
// that makes our procedure single-sited
int round = -1;
int round_limit = 4;
PartitionTree ptree = null;
while (true) {
if (++round > round_limit)
break;
// Get the list of candidate roots and create a partition tree
List<DesignerVertex> candidate_roots = null;
try {
candidate_roots = this.createCandidateRoots(proc_hints, agraph);
} catch (Exception ex) {
LOG.fatal(ex.getLocalizedMessage());
ex.printStackTrace();
System.exit(1);
}
ptree = new PartitionTree(info.catalogContext.database);
ptree.setName("PartTree-Round" + round);
//
// Make sure we include the replicated tables
//
for (String table_name : proc_hints.force_replication) {
DesignerVertex vertex = agraph.getVertex(info.catalogContext.database.getTables().get(table_name));
ptree.addVertex(vertex);
vertex.setAttribute(ptree, PartitionTree.VertexAttributes.METHOD.name(), PartitionMethodType.REPLICATION);
} // FOR
try {
for (DesignerVertex root : candidate_roots) {
buildPartitionTree(ptree, root, agraph, proc_hints);
} // FOR
} catch (Exception ex) {
LOG.fatal(ex.getLocalizedMessage());
ex.printStackTrace();
System.exit(1);
}
//
// We add the procedure that was used to generate this ptree for
// debugging
// Weight the partition trees by how often the procedure is executed
//
ptree.getProcedures().add(catalog_proc);
ptree.setWeight((double) info.workload.getTraces(catalog_proc).size());
designer.getGraphs(catalog_proc).add(ptree);
//
// Now go through and see if there any tables that need to be
// replicated
//
LOG.debug("Invoking replication tree generation...");
AbstractDirectedGraph<DesignerVertex, DesignerEdge> rtree = new AbstractDirectedGraph<DesignerVertex, DesignerEdge>(info.catalogContext.database) {
private static final long serialVersionUID = 1L;
};
rtree.setName("RepTree-Round" + round);
ReplicationTreeGenerator replication_check = new ReplicationTreeGenerator(info, agraph, ptree);
try {
replication_check.generate(rtree);
} catch (Exception ex) {
LOG.fatal(ex.getLocalizedMessage());
ex.printStackTrace();
System.exit(1);
}
designer.getGraphs(catalog_proc).add(rtree);
//
// If there are no tables that are conflicted, then there is nothing
// else we can do
//
if (replication_check.getConflictVertices().isEmpty())
break;
//
// Examine the edges that we created and see if there is a common
// ancestor that the
// the destination vertex wants to be partitioned on
//
List<Table> candidates = new ArrayList<Table>();
candidates.addAll(replication_check.getReplicationCandidates());
boolean forced_dependency = false;
for (DesignerVertex conflict_vertex : replication_check.getConflictVertices()) {
Table conflict_tbl = conflict_vertex.getCatalogItem();
//
// For each edge in the replication tree that is coming into
// this conflict vertex,
// see whether there is an overlapping ancestor
//
Map<Column, Integer> ancestors = new HashMap<Column, Integer>();
int max_count = 0;
Column max_column = null;
Column max_conflict_column = null;
for (DesignerEdge conflict_edge : rtree.getInEdges(conflict_vertex)) {
PredicatePairs cset = (PredicatePairs) conflict_edge.getAttribute(AccessGraph.EdgeAttributes.COLUMNSET.name());
for (Column conflict_column : cset.findAllForParent(Column.class, conflict_tbl)) {
Column ancestor_column = CollectionUtil.last(info.dependencies.getAncestors(conflict_column));
Integer count = ancestors.get(ancestor_column);
count = (count == null ? 1 : count + 1);
ancestors.put(ancestor_column, count);
if (count > max_count) {
max_count = count;
max_column = ancestor_column;
max_conflict_column = conflict_column;
}
} // FOR
} // FOR
assert (max_column != null);
//
// If we have a column that is used in both trees, then that's
// the one we'll want to partition
// our buddy on...
//
boolean valid = true;
for (Column column : ancestors.keySet()) {
if (!max_column.equals(column) && max_count == ancestors.get(column)) {
valid = false;
break;
}
} // FOR
LOG.debug("CONFLICT - " + conflict_vertex + ": " + ancestors);
if (valid) {
String child_key = CatalogKey.createKey(max_conflict_column.getParent());
String parent_key = CatalogKey.createKey(max_column.getParent());
String orig_parent_key = proc_hints.force_dependency.put(child_key, parent_key);
if (!parent_key.equals(orig_parent_key)) {
LOG.debug("Forcing dependency " + child_key + "->" + parent_key);
forced_dependency = true;
}
}
} // FOR
if (forced_dependency)
continue;
//
// Now for each candidate we need to check whether replicating would
// actually improve
// the performance of this procedure. So first we need to get a
// baseline cost
// of the procedure on the workload
// TODO: Support set sizes greater than 2!!
//
Set<Set<Table>> candidate_sets = new LinkedHashSet<Set<Table>>();
assert (candidates.size() <= 2);
for (int ctr0 = 0, cnt = candidates.size(); ctr0 < cnt; ctr0++) {
Set<Table> set = new HashSet<Table>();
set.add(candidates.get(ctr0));
candidate_sets.add(set);
for (int ctr1 = ctr0 + 1; ctr1 < cnt; ctr1++) {
set = new HashSet<Table>();
set.add(candidates.get(ctr0));
set.add(candidates.get(ctr1));
candidate_sets.add(set);
} // FOR
} // FOR
//
// Calculate the cost of each replication candidate set
// If this is the first time we are doing this for this procedure,
// then add in a blank
// replication set so that we can get the baseline cost
//
Set<Table> best_set = null;
double best_cost = Double.MAX_VALUE;
if (best_cost == overall_best_cost)
candidate_sets.add(new HashSet<Table>());
for (Set<Table> replication_set : candidate_sets) {
cost_model.invalidateCache(candidates);
Catalog new_catalog = CatalogCloner.cloneBaseCatalog(info.catalogContext.database.getCatalog());
for (Table catalog_tbl : proc_tables) {
DesignerVertex vertex = ptree.getVertex(catalog_tbl);
assert (vertex != null) : "PartitionTree is missing a vertex for " + catalog_tbl + " " + ptree.getVertices();
Table new_catalog_tbl = CatalogCloner.clone(catalog_tbl, new_catalog);
//
// Mark the table as replicated if it's in the current set
//
if (replication_set.contains(catalog_tbl) || ptree.isReplicated(vertex)) {
new_catalog_tbl.setIsreplicated(true);
new_catalog_tbl.setPartitioncolumn(ReplicatedColumn.get(new_catalog_tbl));
//
// Otherwise partition it according to the current
// partition tree