/** * */ package edu.washington.escience.myria.perfenforce; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.slf4j.LoggerFactory; import com.google.common.base.Joiner; import com.google.common.primitives.Ints; import edu.washington.escience.myria.RelationKey; import edu.washington.escience.myria.Schema; import edu.washington.escience.myria.Type; import edu.washington.escience.myria.parallel.Server; /** * Helper Methods for PerfEnforce */ public class PerfEnforceUtils { protected static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(PerfEnforceUtils.class); /** * Returns a subset of workers * * @param limit the upper bound for the range of workers * @return the resulting set of worker ids */ public static Set<Integer> getWorkerRangeSet(final int limit) { Set<Integer> seq = new HashSet<Integer>(); for (int i = 1; i <= limit; i++) { seq.add(i); } return seq; } /** * Returns an array of worker ids * * @param min the min worker id (inclusive) * @param max the max worker id (inclusive) * @return the resulting array of worker ids */ public static int[] getRangeInclusiveArray(final int min, final int max) { int numberElements = (max - min) + 1; int[] intArray = new int[numberElements]; for (int i = 0; i < numberElements; i++) { intArray[i] = min + i; } return intArray; } /** * Creates a UNION sql statement based on a set of relationKeys * * @param keysToUnion the relationKeys to union * @return the UNION sql statement across a set of relations */ public static String createUnionQuery(final List<RelationKey> keysToUnion) { String sql = ""; for (RelationKey table : keysToUnion) { sql += String.format( "select * from \"%s:%s:%s\"", table.getUserName(), table.getProgramName(), table.getRelationName()); if (table != keysToUnion.get(keysToUnion.size() - 1)) { sql += " UNION ALL "; } } return sql; } /** * Concatenates an array of attributes in a string * * @param keys the set of columns to concatenate * @param schema the schema of the relation * @return the string representation of the schema separated by commas * */ public static String getAttributeKeyString(final Set<Integer> keys, final Schema schema) { return Joiner.on(",").join(getAttributeKeySchema(keys, schema).getColumnNames()); } /** * Creates a new schema based on the given column ids * * @param keys the set of columns to concatenate * @param schema the schema to create * @return the schema for a subset of columns */ public static Schema getAttributeKeySchema(final Set<Integer> keys, final Schema schema) { return schema.getSubSchema(Ints.toArray(keys)); } /** * This method runs a SQL worker on a subset of workers. It will return the features with the highest cost to account for skew (if any) * * @param server an instance of the server * @param sqlQuery the SQL statement to run * @param configuration the configuration * @return the features with the highest cost * @throws PerfEnforceException */ public static String getMaxFeature(final Server server, String sqlQuery, final int configuration) throws PerfEnforceException { try { String explainQuery = "EXPLAIN " + sqlQuery; List<String> featuresList = new ArrayList<String>(); String maxFeature = ""; double maxCost = Integer.MIN_VALUE; String[] allWorkerFeatures = server.executeSQLStatement( explainQuery, Schema.ofFields("explain", Type.STRING_TYPE), PerfEnforceUtils.getWorkerRangeSet(configuration)); for (String currentWorkerFeatures : allWorkerFeatures) { if (currentWorkerFeatures.contains("cost")) { currentWorkerFeatures = currentWorkerFeatures.substring(currentWorkerFeatures.indexOf("cost")); currentWorkerFeatures = currentWorkerFeatures .replace("\"", " ") .replaceAll(".*cost=|\\)", "") .replaceAll("\\.\\.| rows=| width=", ","); if (explainQuery.contains("WHERE")) { String[] tables = explainQuery .substring(explainQuery.indexOf("FROM"), explainQuery.indexOf("WHERE")) .split(","); currentWorkerFeatures = tables.length + "," + currentWorkerFeatures; } else { currentWorkerFeatures = "1," + currentWorkerFeatures; } currentWorkerFeatures += "," + configuration + ",0"; featuresList.add(currentWorkerFeatures); } } for (String f : featuresList) { double cost = Double.parseDouble(f.split(",")[2]); if (cost > maxCost) { maxCost = cost; maxFeature = f; } } return maxFeature; } catch (Exception e) { throw new PerfEnforceException("Error collecting the max feature"); } } }