Repository: phoenix
Updated Branches:
  refs/heads/4.x-HBase-1.0 3381de474 -> bf80d8e69


PHOENIX-2182 - Pherf - Add ability to compare of runs


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/bf80d8e6
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/bf80d8e6
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/bf80d8e6

Branch: refs/heads/4.x-HBase-1.0
Commit: bf80d8e69ea8a11626daa1a1e293d6f68b138e75
Parents: 3381de4
Author: Mujtaba <mujt...@apache.org>
Authored: Mon Sep 21 13:02:37 2015 -0700
Committer: Mujtaba <mujt...@apache.org>
Committed: Mon Sep 21 13:02:37 2015 -0700

----------------------------------------------------------------------
 phoenix-pherf/config/pherf.properties           |   8 +-
 .../java/org/apache/phoenix/pherf/Pherf.java    |  23 +-
 .../phoenix/pherf/result/QueryResult.java       |  32 +-
 .../phoenix/pherf/result/ResultManager.java     |  19 +-
 .../apache/phoenix/pherf/result/ResultUtil.java |  12 +-
 .../phoenix/pherf/result/file/Header.java       |   2 +-
 .../pherf/util/GoogleChartGenerator.java        | 361 +++++++++++++++++++
 .../apache/phoenix/pherf/util/PhoenixUtil.java  |  30 ++
 .../pherf/workload/MultiThreadedRunner.java     |   4 +-
 .../pherf/workload/MultithreadedDiffer.java     |   4 +-
 .../phoenix/pherf/workload/QueryExecutor.java   |  12 +-
 .../phoenix/pherf/workload/QueryVerifier.java   |  30 --
 12 files changed, 494 insertions(+), 43 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/bf80d8e6/phoenix-pherf/config/pherf.properties
----------------------------------------------------------------------
diff --git a/phoenix-pherf/config/pherf.properties 
b/phoenix-pherf/config/pherf.properties
index 152e09c..de5c167 100644
--- a/phoenix-pherf/config/pherf.properties
+++ b/phoenix-pherf/config/pherf.properties
@@ -30,4 +30,10 @@ pherf.default.dataloader.threadpool=0
 pherf.default.dataloader.batchsize=1000
 
 # Directory where results from a scenario run will be written
-pherf.default.results.dir=RESULTS
\ No newline at end of file
+pherf.default.results.dir=RESULTS
+
+# Google chart summary html file
+pherf.default.summary.file=RESULTS/summary.html
+
+# Threshold for comparator to fail. ex. 0.5 equates to 50%
+pherf.default.comparison.threshold=0.45
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/phoenix/blob/bf80d8e6/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/Pherf.java
----------------------------------------------------------------------
diff --git a/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/Pherf.java 
b/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/Pherf.java
index 70fdb11..f6ed19d 100644
--- a/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/Pherf.java
+++ b/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/Pherf.java
@@ -22,7 +22,9 @@ import org.apache.commons.cli.*;
 import org.apache.phoenix.pherf.PherfConstants.GeneratePhoenixStats;
 import org.apache.phoenix.pherf.configuration.XMLConfigParser;
 import org.apache.phoenix.pherf.jmx.MonitorManager;
+import org.apache.phoenix.pherf.result.ResultUtil;
 import org.apache.phoenix.pherf.schema.SchemaReader;
+import org.apache.phoenix.pherf.util.GoogleChartGenerator;
 import org.apache.phoenix.pherf.util.PhoenixUtil;
 import org.apache.phoenix.pherf.util.ResourceList;
 import org.apache.phoenix.pherf.workload.QueryExecutor;
@@ -45,6 +47,8 @@ public class Pherf {
 
     static {
         options.addOption("disableSchemaApply", false, "Set to disable schema 
from being applied.");
+               options.addOption("disableRuntimeResult", false,
+                               "Set to disable writing detailed CSV file 
during query execution. Those will eventually get written at the end of query 
execution.");
         options.addOption("z", "zookeeper", true,
                 "HBase Zookeeper address for connection. Default: localhost");
         options.addOption("q", "query", false, "Executes multi-threaded query 
sets");
@@ -76,6 +80,8 @@ public class Pherf {
         options.addOption("d", "debug", false, "Put tool in debug mode");
         options.addOption("stats", false,
                 "Update Phoenix Statistics after data is loaded with -l 
argument");
+               options.addOption("label", true, "Label a run. Result file name 
will be suffixed with specified label");
+               options.addOption("compare", true, "Specify labeled runs to 
compare against current run");
     }
 
     private final String zookeeper;
@@ -92,7 +98,10 @@ public class Pherf {
     private final int rowCountOverride;
     private final boolean listFiles;
     private final boolean applySchema;
+    private final boolean writeRuntimeResults;
     private final GeneratePhoenixStats generateStatistics;
+    private final String label;
+    private final String compareResults;
 
     public Pherf(String[] args) throws Exception {
         CommandLineParser parser = new PosixParser();
@@ -126,6 +135,7 @@ public class Pherf {
         isFunctional = command.hasOption("diff");
         listFiles = command.hasOption("listFiles");
         applySchema = !command.hasOption("disableSchemaApply");
+        writeRuntimeResults = !command.hasOption("disableRuntimeResult");
         scenarioFile =
                 command.hasOption("scenarioFile") ? 
command.getOptionValue("scenarioFile") : null;
         schemaFile = command.hasOption("schemaFile") ? 
command.getOptionValue("schemaFile") : null;
@@ -136,6 +146,8 @@ public class Pherf {
                 command.getOptionValue("writerThreadSize",
                         
properties.getProperty("pherf.default.dataloader.threadpool"));
         properties.setProperty("pherf. default.dataloader.threadpool", 
writerThreadPoolSize);
+        label = command.getOptionValue("label", null);
+        compareResults = command.getOptionValue("compare", null);
 
         if ((command.hasOption("h") || (args == null || args.length == 0)) && 
!command
                 .hasOption("listFiles")) {
@@ -144,6 +156,7 @@ public class Pherf {
         }
         PhoenixUtil.setZookeeper(zookeeper);
         PhoenixUtil.setRowCountOverride(rowCountOverride);
+        ResultUtil.setFileSuffix(label);
     }
 
     public static void main(String[] args) {
@@ -179,6 +192,14 @@ public class Pherf {
                 }
                 return;
             }
+            
+            // Compare results and exit  
+                       if (null != compareResults) {
+                               logger.info("\nStarting to compare results and 
exiting for " + compareResults);
+                               new 
GoogleChartGenerator(compareResults).readAndRender();
+                               return;
+            }
+            
             XMLConfigParser parser = new XMLConfigParser(scenarioFile);
 
             // Drop tables with PHERF schema and regex comparison
@@ -225,7 +246,7 @@ public class Pherf {
 
                 workloadExecutor
                         .add(new QueryExecutor(parser, phoenixUtil, 
workloadExecutor, parser.getDataModels(), queryHint,
-                                isFunctional));
+                                isFunctional, writeRuntimeResults));
 
             } else {
                 logger.info(

http://git-wip-us.apache.org/repos/asf/phoenix/blob/bf80d8e6/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/result/QueryResult.java
----------------------------------------------------------------------
diff --git 
a/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/result/QueryResult.java 
b/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/result/QueryResult.java
index c0b4bf7..669a472 100644
--- 
a/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/result/QueryResult.java
+++ 
b/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/result/QueryResult.java
@@ -20,15 +20,18 @@ package org.apache.phoenix.pherf.result;
 
 import org.apache.phoenix.pherf.configuration.Query;
 import org.apache.phoenix.pherf.result.file.ResultFileDetails;
+import org.apache.phoenix.pherf.util.PhoenixUtil;
 import org.apache.phoenix.util.DateUtil;
 
+import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 
 public class QueryResult extends Query {
     private List<ThreadTime> threadTimes = new ArrayList<>();
-
+    private static PhoenixUtil pUtil = PhoenixUtil.create();
+    
     public QueryResult() {
         super();
     }
@@ -106,8 +109,35 @@ public class QueryResult extends Query {
         rowValues.add(new 
ResultValue(util.convertNull(String.valueOf(getAvgRunTimeInMs()))));
         rowValues.add(new 
ResultValue(util.convertNull(String.valueOf(getAvgMinRunTimeInMs()))));
         rowValues.add(new 
ResultValue(util.convertNull(String.valueOf(getRunCount()))));
+        rowValues.add(new 
ResultValue(util.convertNull(String.valueOf(getExplainPlan()))));
+        rowValues.add(new 
ResultValue(util.convertNull(String.valueOf(getResultRowCount()))));
         return rowValues;
     }
+    
+    private String getExplainPlan() {
+       try {
+                       return pUtil.getExplainPlan(this);
+               } catch (SQLException e) {
+                       e.printStackTrace();
+               }
+       return null;
+    }
+    
+    private long getResultRowCount() {
+        long resultRowCount = -1;
+        for (ThreadTime tt : getThreadTimes()) {
+               for (int i = 0; i < tt.getRunTimesInMs().size(); i++) {
+                       if (resultRowCount == -1) {
+                               resultRowCount = 
tt.getRunTimesInMs().get(i).getResultRowCount();
+                       } else {
+                               if (resultRowCount != 
tt.getRunTimesInMs().get(i).getResultRowCount()) {
+                                       return -1;
+                               }
+                       }
+               }
+        }
+        return resultRowCount;
+    }
 
     public List<List<ResultValue>> getCsvDetailedRepresentation(ResultUtil 
util, ResultFileDetails details) {
         List<List<ResultValue>> rows = new ArrayList<>();

http://git-wip-us.apache.org/repos/asf/phoenix/blob/bf80d8e6/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/result/ResultManager.java
----------------------------------------------------------------------
diff --git 
a/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/result/ResultManager.java
 
b/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/result/ResultManager.java
index 6a79486..f994621 100644
--- 
a/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/result/ResultManager.java
+++ 
b/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/result/ResultManager.java
@@ -32,7 +32,8 @@ public class ResultManager {
     private final List<ResultHandler> resultHandlers;
     private final ResultUtil util;
     private static final List<ResultHandler> defaultHandlers;
-
+    private static final List<ResultHandler> minimalHandlers;
+    
     static {
         defaultHandlers = new ArrayList<>();
         XMLResultHandler xmlResultHandler = new XMLResultHandler();
@@ -51,9 +52,23 @@ public class ResultManager {
         
handlerDet.setResultFileDetails(ResultFileDetails.CSV_DETAILED_PERFORMANCE);
         defaultHandlers.add(handlerDet);
     }
+    
+    static {
+       minimalHandlers = new ArrayList<>();
+        ImageResultHandler imageResultHandler = new ImageResultHandler();
+        imageResultHandler.setResultFileDetails(ResultFileDetails.IMAGE);
+        minimalHandlers.add(imageResultHandler);
+    }
 
     public ResultManager(String fileNameSeed) {
-        this(fileNameSeed, InstanceResolver.get(ResultHandler.class, 
defaultHandlers));
+        this(fileNameSeed, true);
+    }
+    
+    @SuppressWarnings("unchecked")
+       public ResultManager(String fileNameSeed, boolean writeRuntimeResults) {
+        this(fileNameSeed, writeRuntimeResults ?
+                       InstanceResolver.get(ResultHandler.class, 
defaultHandlers) :
+                       InstanceResolver.get(ResultHandler.class, 
minimalHandlers));
     }
 
     public ResultManager(String fileNameSeed, List<ResultHandler> 
resultHandlers) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/bf80d8e6/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/result/ResultUtil.java
----------------------------------------------------------------------
diff --git 
a/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/result/ResultUtil.java 
b/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/result/ResultUtil.java
index 9a589f5..04e6197 100644
--- 
a/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/result/ResultUtil.java
+++ 
b/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/result/ResultUtil.java
@@ -153,9 +153,9 @@ public class ResultUtil {
         if (null == FILE_SUFFIX) {
             Date date = new Date();
             Format formatter = new SimpleDateFormat("YYYY-MM-dd_hh-mm-ss");
-            FILE_SUFFIX = "_" + formatter.format(date);
+            FILE_SUFFIX = formatter.format(date);
         }
-        return FILE_SUFFIX;
+        return "_" + FILE_SUFFIX;
     }
 
     public String convertNull(String str) {
@@ -236,4 +236,12 @@ public class ResultUtil {
         }
         return sb.toString();
     }
+    
+    /**
+     * Set the file suffix
+     * @param suffix
+     */
+    public static void setFileSuffix(String suffix) {
+       FILE_SUFFIX = suffix;
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/phoenix/blob/bf80d8e6/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/result/file/Header.java
----------------------------------------------------------------------
diff --git 
a/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/result/file/Header.java 
b/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/result/file/Header.java
index 15e2b9a..066fa7a 100644
--- 
a/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/result/file/Header.java
+++ 
b/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/result/file/Header.java
@@ -21,7 +21,7 @@ package org.apache.phoenix.pherf.result.file;
 public enum Header {
     EMPTY(""),
     AGGREGATE_PERFORMANCE(
-            
"START_TIME,QUERY_GROUP,QUERY,TENANT_ID,AVG_MAX_TIME_MS,AVG_TIME_MS,AVG_MIN_TIME_MS,RUN_COUNT"),
+            
"START_TIME,QUERY_GROUP,QUERY,TENANT_ID,AVG_MAX_TIME_MS,AVG_TIME_MS,AVG_MIN_TIME_MS,RUN_COUNT,EXPLAIN_PLAN,RESULT_ROW_COUNT"),
     DETAILED_BASE(
             
"BASE_TABLE_NAME,SCENARIO_NAME,ZOOKEEPER,ROW_COUNT,EXECUTION_COUNT,EXECUTION_TYPE,PHOENIX_PROPERTIES"
                     + 
",START_TIME,QUERY_GROUP,QUERY,TENANT_ID,THREAD_NUMBER,CONCURRENCY_LEVEL"),

http://git-wip-us.apache.org/repos/asf/phoenix/blob/bf80d8e6/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/util/GoogleChartGenerator.java
----------------------------------------------------------------------
diff --git 
a/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/util/GoogleChartGenerator.java
 
b/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/util/GoogleChartGenerator.java
new file mode 100644
index 0000000..37f29e7
--- /dev/null
+++ 
b/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/util/GoogleChartGenerator.java
@@ -0,0 +1,361 @@
+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 GoogleChartGenerator {
+
+       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 GoogleChartGenerator(String labels) {
+               this.setLabels(labels);
+       }
+       
+       String[] getLabels() {
+               return labels;
+       }
+
+       void setLabels(String[] labels) {
+               this.labels = labels;
+       }
+
+       void setLabels(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 (timeToCompare < 10) { // extremely small 
query time in ms therefore don't compare
+                                       return true;
+                               }
+                       }
+                               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 summaryFile = 
PherfConstants.create().getProperty("pherf.default.summary.file");
+       String title = labels[0];
+       PrintWriter writer = new PrintWriter(summaryFile, "UTF-8");
+       
+       
writer.println(StaticGoogleChartsRenderingData.HEADER.replace("[title]", 
title));
+       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]", title);
+       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>"
+                                       + explainPlan.replace("'", "") + 
"</font><br>";
+               }
+               
+               void setExplainPlan(String explainPlan) {
+                       this.explainPlan = explainPlan;
+               }
+               long getMinTime() {
+                       if (minTime <= 2) 
+                               return 2;
+                       else
+                               return minTime;
+               }
+               public String getMinTimeAsHTML() {
+                       return "<font face=arial size=1><b>MIN TIME 
</b></font><font face=arial size=3>"
+                                       + minTime
+                                       + " ms ("
+                                       + df.format((double) minTime / 1000)
+                                       + " sec)</font><br>";
+               }
+               void setMinTime(long minTime) {
+                       this.minTime = minTime;
+               }
+               long getAvgTime() {
+                       return avgTime;
+               }
+               public String getAvgTimeAsHTML() {
+                       return "<font face=arial size=1><b>AVERAGE TIME 
</b></font><font face=arial size=3>"
+                                       + avgTime
+                                       + " ms ("
+                                       + df.format((double) avgTime / 1000)
+                                       + " sec)</font><br>";
+               }
+               void setAvgTime(long avgTime) {
+                       this.avgTime = avgTime;
+               }
+
+               public long getNumRuns() {
+                       return numRuns;
+               }
+               public String getNumRunsAsHTML() {
+                       return "<font face=arial size=1><b>NUMBER OF RUNS 
</b></font><font face=arial size=3>"
+                                       + numRuns + "</font><br>";
+               }
+
+               public void setNumRuns(long numRuns) {
+                       this.numRuns = numRuns;
+               }
+
+               public String getQuery() {
+                       return query;
+               }
+
+               public String getQueryAsHTML() {
+                       return "<br><font face=arial size=1><b>QUERY </b>"
+                                       + query.replace("'", "") + " (TENANT 
ID: " + getTenantId()
+                                       + ")</font><br>";
+               }
+               
+               public void setQuery(String query) {
+                       this.query = query;
+               }
+
+               public String getTenantId() {
+                       return tenantId;
+               }
+               
+               public void setTenantId(String tenantId) {
+                       this.tenantId = tenantId;
+               }
+
+               public String getLabel() {
+                       return label;
+               }
+               
+               public String getLabelAsHTML() {
+                       return "<font face=arial size=4 color=#666699>" + label
+                                       + "</font><br>";
+               }
+
+               public void setLabel(String label) {
+                       this.label = label;
+               }
+
+               public long getRowCount() {
+                       return rowCount;
+               }
+               public String getRowCountAsHTML() {
+                       return "<font face=arial size=1><b>RESULT ROW COUNT 
</b></font><font face=arial size=3>"
+                                       + rowCount + "</font><br>";
+               }
+
+               public void setRowCount(long rowCount) {
+                       this.rowCount = rowCount;
+               }
+    }
+    
+    static class StaticGoogleChartsRenderingData {
+               public static String HEADER = 
"<html><head><title>[title]</title>"
+                               + "<script type='text/javascript' 
src='https://www.google.com/jsapi'></script>"
+                               + "<script 
type='text/javascript'>google.load('visualization', '1', {packages: 
['corechart', 'bar']});google.setOnLoadCallback(drawMaterial);"
+                               + "function drawMaterial() {var dataTable = new 
google.visualization.DataTable();dataTable.addColumn('string', 'Query');";
+               public static String FOOTER = "var options = {title: 
'',titleTextStyle: {color: 'gray', fontName: 'Raleway', fontSize: '24'},hAxis: 
{title: 'Minimum query time for all runs in milli-seconds (ms) | Scaled 
logrithmic | Hover to see details',titleTextStyle: {italic: false,fontName: 
'arial', fontSize: '12'},"
+                               + "logScale: true,minValue: 0,textStyle: { 
fontName: 'arial', fontSize: '14'},},vAxis: {textStyle: {fontName: 'arial', 
fontSize: '12', fontWidth: 'normal', paddingRight: '100',marginRight: '100'}},"
+                               + "chartArea: {left:300, width: 500, right: 
400, top: 50, height: 700},"
+                               + "legend:{textStyle:{fontSize:'13', 
fontName:'arial'}},"
+                               + "tooltip: {isHtml: true},width: 1200,height: 
800,"
+                               + "bars:'horizontal',bar: { groupWidth: '75%' 
},colors: ['#E1A5A9', '#A9A5BC', '#A9A5E1']};"
+                               + "var material = new 
google.visualization.BarChart(document.getElementById('chart_div'));"
+                               + "material.draw(dataTable, options);}"
+                               + "</script></head><body><b><font face=raleway 
size=5>PHERFED [title]</font><br><font face=raleway 
size=4>[summary]</font></b><div id='chart_div' 
style='margin:0px;padding:0px;'></div></body></html>";
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/bf80d8e6/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/util/PhoenixUtil.java
----------------------------------------------------------------------
diff --git 
a/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/util/PhoenixUtil.java 
b/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/util/PhoenixUtil.java
index 57858a3..1f610f6 100644
--- a/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/util/PhoenixUtil.java
+++ b/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/util/PhoenixUtil.java
@@ -300,4 +300,34 @@ public class PhoenixUtil {
 
         return manager;
     }
+    
+    /**
+     * Get explain plan for a query
+     *
+     * @param query
+     * @return
+     * @throws SQLException
+     */
+    public String getExplainPlan(Query query) throws SQLException {
+        Connection conn = null;
+        ResultSet rs = null;
+        PreparedStatement statement = null;
+        StringBuilder buf = new StringBuilder();
+        try {
+            conn = getConnection(query.getTenantId());
+            statement = conn.prepareStatement("EXPLAIN " + 
query.getStatement());
+            rs = statement.executeQuery();
+            while (rs.next()) {
+                buf.append(rs.getString(1).trim().replace(",", "-"));
+            }
+            statement.close();
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (rs != null) rs.close();
+            if (statement != null) statement.close();
+            if (conn != null) conn.close();
+        }
+        return buf.toString();
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/bf80d8e6/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/workload/MultiThreadedRunner.java
----------------------------------------------------------------------
diff --git 
a/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/workload/MultiThreadedRunner.java
 
b/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/workload/MultiThreadedRunner.java
index 524724c..de6cdaf 100644
--- 
a/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/workload/MultiThreadedRunner.java
+++ 
b/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/workload/MultiThreadedRunner.java
@@ -57,14 +57,14 @@ class MultiThreadedRunner implements Runnable {
      * @param executionDurationInMs
      */
     MultiThreadedRunner(String threadName, Query query, DataModelResult 
dataModelResult,
-            ThreadTime threadTime, long numberOfExecutions, long 
executionDurationInMs) {
+            ThreadTime threadTime, long numberOfExecutions, long 
executionDurationInMs, boolean writeRuntimeResults) {
         this.query = query;
         this.threadName = threadName;
         this.threadTime = threadTime;
         this.dataModelResult = dataModelResult;
         this.numberOfExecutions = numberOfExecutions;
         this.executionDurationInMs = executionDurationInMs;
-        this.resultManager = new ResultManager(dataModelResult.getName());
+               this.resultManager = new 
ResultManager(dataModelResult.getName(), writeRuntimeResults);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/phoenix/blob/bf80d8e6/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/workload/MultithreadedDiffer.java
----------------------------------------------------------------------
diff --git 
a/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/workload/MultithreadedDiffer.java
 
b/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/workload/MultithreadedDiffer.java
index 91189e2..decff51 100644
--- 
a/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/workload/MultithreadedDiffer.java
+++ 
b/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/workload/MultithreadedDiffer.java
@@ -25,6 +25,7 @@ import org.apache.phoenix.pherf.PherfConstants;
 import org.apache.phoenix.pherf.configuration.Query;
 import org.apache.phoenix.pherf.result.RunTime;
 import org.apache.phoenix.pherf.result.ThreadTime;
+import org.apache.phoenix.pherf.util.PhoenixUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -36,6 +37,7 @@ class MultithreadedDiffer implements Runnable {
     private long numberOfExecutions;
     private long executionDurationInMs;
     private QueryVerifier queryVerifier = new QueryVerifier(true);
+    private static PhoenixUtil pUtil = PhoenixUtil.create();
 
     private synchronized ThreadTime getThreadTime() {
         return threadTime;
@@ -51,7 +53,7 @@ class MultithreadedDiffer implements Runnable {
         Date startDate = Calendar.getInstance().getTime();
         String newCSV = queryVerifier.exportCSV(query);
         boolean verifyResult = queryVerifier.doDiff(query, newCSV);
-        String explainPlan = queryVerifier.getExplainPlan(query);
+        String explainPlan = pUtil.getExplainPlan(query);
         getThreadTime().getRunTimesInMs().add(new RunTime(
                         verifyResult == true ? PherfConstants.DIFF_PASS : 
PherfConstants.DIFF_FAIL,
                         explainPlan, startDate, -1L, (int) 
(System.currentTimeMillis() - start)));

http://git-wip-us.apache.org/repos/asf/phoenix/blob/bf80d8e6/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/workload/QueryExecutor.java
----------------------------------------------------------------------
diff --git 
a/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/workload/QueryExecutor.java
 
b/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/workload/QueryExecutor.java
index 50d7190..347e203 100644
--- 
a/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/workload/QueryExecutor.java
+++ 
b/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/workload/QueryExecutor.java
@@ -41,21 +41,29 @@ public class QueryExecutor implements Workload {
     private final XMLConfigParser parser;
     private final PhoenixUtil util;
     private final WorkloadExecutor workloadExecutor;
+    private final boolean writeRuntimeResults;
 
     public QueryExecutor(XMLConfigParser parser, PhoenixUtil util,
             WorkloadExecutor workloadExecutor) {
-        this(parser, util, workloadExecutor, parser.getDataModels(), null, 
false);
+        this(parser, util, workloadExecutor, parser.getDataModels(), null, 
false, true);
     }
 
     public QueryExecutor(XMLConfigParser parser, PhoenixUtil util,
             WorkloadExecutor workloadExecutor, List<DataModel> dataModels, 
String queryHint,
             boolean exportCSV) {
+       this(parser, util, workloadExecutor, dataModels, queryHint, exportCSV, 
true);
+    }
+
+    public QueryExecutor(XMLConfigParser parser, PhoenixUtil util,
+            WorkloadExecutor workloadExecutor, List<DataModel> dataModels, 
String queryHint,
+            boolean exportCSV, boolean writeRuntimeResults) {
         this.parser = parser;
         this.queryHint = queryHint;
         this.exportCSV = exportCSV;
         this.dataModels = dataModels;
         this.util = util;
         this.workloadExecutor = workloadExecutor;
+        this.writeRuntimeResults = writeRuntimeResults;
     }
 
     @Override
@@ -262,7 +270,7 @@ public class QueryExecutor implements Workload {
             thread =
                     new MultiThreadedRunner(threadTime.getThreadName(), 
queryResult,
                             dataModelResult, threadTime, 
querySet.getNumberOfExecutions(),
-                            querySet.getExecutionDurationInMs());
+                            querySet.getExecutionDurationInMs(), 
writeRuntimeResults);
         } else {
             thread =
                     new MultithreadedDiffer(threadTime.getThreadName(), 
queryResult, threadTime,

http://git-wip-us.apache.org/repos/asf/phoenix/blob/bf80d8e6/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/workload/QueryVerifier.java
----------------------------------------------------------------------
diff --git 
a/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/workload/QueryVerifier.java
 
b/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/workload/QueryVerifier.java
index c9333a0..7b2bb12 100644
--- 
a/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/workload/QueryVerifier.java
+++ 
b/phoenix-pherf/src/main/java/org/apache/phoenix/pherf/workload/QueryVerifier.java
@@ -141,36 +141,6 @@ public class QueryVerifier {
     }
 
     /**
-     * Get explain plan for a query
-     *
-     * @param query
-     * @return
-     * @throws SQLException
-     */
-    public String getExplainPlan(Query query) throws SQLException {
-        Connection conn = null;
-        ResultSet rs = null;
-        PreparedStatement statement = null;
-        StringBuilder buf = new StringBuilder();
-        try {
-            conn = pUtil.getConnection(query.getTenantId());
-            statement = conn.prepareStatement("EXPLAIN " + 
query.getStatement());
-            rs = statement.executeQuery();
-            while (rs.next()) {
-                buf.append(rs.getString(1).trim().replace(",", "-"));
-            }
-            statement.close();
-        } catch (Exception e) {
-            e.printStackTrace();
-        } finally {
-            if (rs != null) rs.close();
-            if (statement != null) statement.close();
-            if (conn != null) conn.close();
-        }
-        return buf.toString();
-    }
-
-    /**
      * Helper method to generate CSV file name
      *
      * @param query

Reply via email to