[ 
https://issues.apache.org/jira/browse/PHOENIX-2182?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14726273#comment-14726273
 ] 

ASF GitHub Bot commented on PHOENIX-2182:
-----------------------------------------

Github user codymarcel commented on a diff in the pull request:

    https://github.com/apache/phoenix/pull/115#discussion_r38477085
  
    --- Diff: 
phoenix-pherf/src/main/java/org/apache/phoenix/pherf/util/ResultsComparator.java
 ---
    @@ -0,0 +1,353 @@
    +package org.apache.phoenix.pherf.util;
    +
    +import java.io.FileNotFoundException;
    +import java.io.FileReader;
    +import java.io.PrintWriter;
    +import java.io.UnsupportedEncodingException;
    +import java.text.DecimalFormat;
    +import java.text.SimpleDateFormat;
    +import java.util.Date;
    +import java.util.LinkedHashMap;
    +import java.util.Map;
    +import java.util.TreeMap;
    +
    +import org.apache.commons.csv.CSVFormat;
    +import org.apache.commons.csv.CSVParser;
    +import org.apache.commons.csv.CSVRecord;
    +import org.apache.phoenix.pherf.PherfConstants;
    +import org.apache.phoenix.pherf.result.file.ResultFileDetails;
    +
    +/**
    + * Compare results based on set threshold and render results as Google 
Charts
    + */
    +public class ResultsComparator {
    +
    +   private String[] labels;
    +   private final Map<String, DataNode> datanodes = new TreeMap<String, 
DataNode>();
    +   private final PherfConstants constants = PherfConstants.create();
    +   private final String resultDir = 
constants.getProperty("pherf.default.results.dir");
    +   private final double threshold = 
Double.parseDouble(constants.getProperty("pherf.default.comparison.threshold"));
    +
    +   public ResultsComparator(String labels) {
    +           this.setLabelsAsString(labels);
    +   }
    +   
    +   String[] getLabels() {
    +           return labels;
    +   }
    +
    +   void setLabels(String[] labels) {
    +           this.labels = labels;
    +   }
    +
    +   void setLabelsAsString(String labels) {
    +           this.labels = labels.split(",");
    +   }
    +   
    +   public void readAndRender() {
    +           try {
    +                   for (String label : labels) {
    +                           read(label);
    +                   }
    +                   renderAsGoogleChartsHTML();
    +
    +           } catch (Exception e) {
    +                   e.printStackTrace();
    +           }
    +   }
    +
    +   /**
    +    * Reads aggregate file and convert it to DataNode 
    +    * @param label
    +    * @throws Exception
    +    */
    +    private void read(String label) throws Exception {
    +           String resultFileName = resultDir 
    +                           + PherfConstants.PATH_SEPARATOR
    +                           + PherfConstants.RESULT_PREFIX 
    +                           + label
    +                           + 
ResultFileDetails.CSV_AGGREGATE_PERFORMANCE.getExtension();
    +
    +           FileReader in = new FileReader(resultFileName);
    +           final CSVParser parser = new CSVParser(in, 
CSVFormat.DEFAULT.withHeader());
    +
    +        for (CSVRecord record : parser) {
    +            String group = record.get("QUERY_GROUP");
    +            String query = record.get("QUERY");
    +            String explain = record.get("EXPLAIN_PLAN");
    +            String tenantId = record.get("TENANT_ID");
    +            long avgTime = Long.parseLong(record.get("AVG_TIME_MS"));
    +            long minTime = Long.parseLong(record.get("AVG_MIN_TIME_MS"));
    +            long numRuns = Long.parseLong(record.get("RUN_COUNT"));
    +            long rowCount = Long.parseLong(record.get("RESULT_ROW_COUNT"));
    +            Node node = new Node(minTime, avgTime, numRuns, explain, 
query, tenantId, label, rowCount);
    +            
    +            if (datanodes.containsKey(group)) {
    +                   datanodes.get(group).getDataSet().put(label, node);
    +            } else {
    +                   datanodes.put(group, new DataNode(label, node));
    +            }
    +        }
    +        parser.close();
    +    }
    +
    +    /**
    +     * Verifies if the first result is within the set 
    +     * threshold of pherf.default.comparison.threshold
    +     * set in pherf.properties files
    +     * @param threshold
    +     * @return
    +     */
    +    private boolean verifyWithinThreshold(double threshold) {
    +           long resetTimeToCompare = -1;
    +           long timeToCompare = resetTimeToCompare;
    +           for (Map.Entry<String, DataNode> dn : datanodes.entrySet()) {   
                
    +           for (Map.Entry<String, Node> node : 
dn.getValue().getDataSet().entrySet()) {
    +                   if (timeToCompare == -1) {
    +                           timeToCompare = node.getValue().getMinTime();
    +                   }
    +                           if ((((double) (timeToCompare - 
node.getValue().getMinTime())) / (double) node
    +                                           .getValue().getMinTime()) > 
threshold) {
    +                                   return false;
    +                           }
    +           }
    +           timeToCompare = resetTimeToCompare;
    +           }
    +           return true;
    +    }
    +    
    +    /**
    +     * Render results as Google charts
    +     * @throws FileNotFoundException
    +     * @throws UnsupportedEncodingException
    +     */
    +    private void renderAsGoogleChartsHTML() throws FileNotFoundException, 
UnsupportedEncodingException {
    +           String lastKeyPrefix = "";
    +           StringBuffer sb = new StringBuffer();
    +           for (String label : labels) {
    +                   sb.append("dataTable.addColumn('number', '" + label + 
"');\n");
    +                   sb.append("dataTable.addColumn({type: 'string', role: 
'tooltip', 'p': {'html': true}});\n");
    +           }
    +           sb.append("dataTable.addRows([\n");
    +           for (Map.Entry<String, DataNode> dn : datanodes.entrySet()) {
    +                   String currentKeyPrefix = dn.getKey().substring(0, 
dn.getKey().indexOf('|'));
    +                   if (!lastKeyPrefix.equalsIgnoreCase(currentKeyPrefix) 
&& lastKeyPrefix != "") {
    +                           sb.append(getBlankRow());
    +                   }
    +                   lastKeyPrefix = currentKeyPrefix;
    +           sb.append("['" + dn.getKey() + "'");
    +           for (Map.Entry<String, Node> nodeSet : 
dn.getValue().getDataSet().entrySet()) {
    +                   sb.append (", " + nodeSet.getValue().getMinTime());
    +                   sb.append (",'" + 
getToolTipAsHTML(dn.getValue().getDataSet()) + "'");
    +           }
    +           sb.append("],\n");
    +        }
    +           String summaryDir = 
PherfConstants.create().getProperty("pherf.default.summary.dir");
    +           PrintWriter writer = new PrintWriter(summaryDir + "/" + 
PherfConstants.SUMMARY_HTML_FILE_NAME, "UTF-8");
    +           writer.println(StaticGoogleChartsRenderingData.HEADER);
    +           writer.println(sb.substring(0, sb.length() - 2) + "\n]);");
    +           String thresholdString = Math.round((threshold*100)) + "%"; 
    +           String footer = StaticGoogleChartsRenderingData.FOOTER
    +                           .replace("[summary]",
    +                                           
((verifyWithinThreshold(threshold) == true ? "<font color=green>PASSED | 
Results are within ": 
    +                                                   "<font color=red>FAILED 
| Results are outside "))
    +                                                           + "set 
threshold of " + thresholdString + "</font><br>"
    +                                                           + new 
SimpleDateFormat("yyyy/MM/dd ha z").format(new Date()));
    +           footer = footer.replace("[title]", labels[0]);
    +           writer.println(footer);
    +           writer.close();
    +    }
    +    
    +    /**
    +     * Render a blank Google charts row
    +     * @return
    +     */
    +    private String getBlankRow() {
    +           String ret = "['" + new String(new char[60]).replace("\0", ".") 
+ "'";
    +           for (int i=0; i<labels.length; i++)
    +                   ret += ",0,''";
    +           ret += "],";
    +           return ret;
    +   }
    +
    +    /**
    +     * Render tooltip as HTML table
    +     * @param nodeDataSet
    +     * @return
    +     */
    +   private String getToolTipAsHTML(Map<String, Node> nodeDataSet) {
    +           String ret = "<table width=1000 cellpadding=1 cellspacing=0 
border=0 bgcolor=#F4F4F4><tr>";
    +           for (Map.Entry<String, Node> nodeSet : nodeDataSet.entrySet())  
    +                   ret += "<td>" + getToolText(nodeSet.getValue()) + 
"</td>";
    +           return ret + "</tr></table>";
    +    }
    +    
    +   /**
    +    * Get tooltip for node
    +    * @param node
    +    * @return
    +    */
    +    private String getToolText(Node node) {
    +        return node.getLabelAsHTML() 
    +                   + node.getAvgTimeAsHTML()
    +                   + node.getMinTimeAsHTML()
    +                   + node.getNumRunsAsHTML()
    +                   + node.getRowCountAsHTML()
    +                   + node.getExplainPlanAsHTML()
    +                   + node.getQueryAsHTML();
    +    }
    +    
    +    /**
    +     * DataNode to store results to render and compare 
    +     */
    +    class DataNode {
    +           private Map<String, Node> dataSet = new LinkedHashMap<String, 
Node>();
    +           
    +           public DataNode(String label, Node node) {
    +                   this.getDataSet().put(label, node);
    +           }
    +           
    +           public Map<String, Node> getDataSet() {
    +                   return dataSet;
    +           }
    +           public void setDataSet(Map<String, Node> dataSet) {
    +                   this.dataSet = dataSet;
    +           }
    +    }
    +    
    +    class Node {
    +           private String explainPlan;
    +           private String query;
    +           private String tenantId;
    +           private long minTime;
    +           private long avgTime;
    +           private long numRuns;
    +           private long rowCount;
    +           private String label;
    +           private DecimalFormat df = new DecimalFormat("#.#");
    +           
    +           public Node(long minTime, long avgTime, long numRuns, String 
explainPlan, String query, String tenantId, String label, long rowCount) {
    +                   this.setMinTime(minTime);
    +                   this.setAvgTime(avgTime);
    +                   this.setNumRuns(numRuns);
    +                   this.setExplainPlan(explainPlan);
    +                   this.setQuery(query);
    +                   this.setTenantId(tenantId);
    +                   this.setLabel(label);
    +                   this.setRowCount(rowCount);
    +           }
    +           
    +           String getExplainPlan() {
    +                   return explainPlan;
    +           }
    +           String getExplainPlanAsHTML() {
    +                   return "</br><font face=arial size=1><b>EXPLAIN PLAN 
</b>"
    --- End diff --
    
    Perhaps we should use an html generation library instead of hard coding all 
this in strings?
    
    Never tried this one, bit looks like it has god template support.
    http://freemarker.org/docs/dgui_quickstart_basics.html



> Pherf - Add ability to compare of run(s) and generate warning if performance 
> degrades beyond set threshold
> ----------------------------------------------------------------------------------------------------------
>
>                 Key: PHOENIX-2182
>                 URL: https://issues.apache.org/jira/browse/PHOENIX-2182
>             Project: Phoenix
>          Issue Type: Improvement
>            Reporter: Mujtaba Chohan
>            Assignee: Mujtaba Chohan
>         Attachments: PHOENIX-2182.patch
>
>
> Add ability to compare of run(s) and generate warning if performance degrades 
> beyond set threshold. This would also need that runs can be labeled for known 
> baselines.



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

Reply via email to