org.apache.phoenix.pherf.util.GoogleChartGenerator Maven / Gradle / Ivy
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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.PherfConstants.CompareType;
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 CompareType compareType;
private final Map datanodes = new TreeMap();
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, CompareType compareType) {
this.setLabels(labels);
this.setCompareType(compareType);
}
String[] getLabels() {
return labels;
}
void setLabels(String[] labels) {
this.labels = labels;
}
void setLabels(String labels) {
this.labels = labels.split(",");
}
CompareType getCompareType() {
return this.compareType;
}
void setCompareType(CompareType compareType) {
this.compareType = compareType;
}
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 dn : datanodes.entrySet()) {
for (Map.Entry node : dn.getValue().getDataSet().entrySet()) {
if (timeToCompare == -1) {
timeToCompare = node.getValue().getTime(getCompareType());
if (timeToCompare < 10) { // extremely small query time in ms therefore don't compare
return true;
}
}
if ((((double) (timeToCompare - node.getValue().getTime(getCompareType()))) / (double) node
.getValue().getTime(getCompareType())) > 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 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 nodeSet : dn.getValue().getDataSet().entrySet()) {
sb.append (", " + nodeSet.getValue().getTime(getCompareType()));
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 ? "PASSED | Results are within ":
"FAILED | Results are outside "))
+ "set threshold of " + thresholdString + "
"
+ 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 nodeDataSet) {
String ret = "";
for (Map.Entry nodeSet : nodeDataSet.entrySet())
ret += "" + getToolText(nodeSet.getValue()) + " ";
return ret + "
";
}
/**
* 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 dataSet = new LinkedHashMap();
public DataNode(String label, Node node) {
this.getDataSet().put(label, node);
}
public Map getDataSet() {
return dataSet;
}
public void setDataSet(Map 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 "EXPLAIN PLAN "
+ explainPlan.replace("'", "") + "
";
}
void setExplainPlan(String explainPlan) {
this.explainPlan = explainPlan;
}
long getTime(CompareType compareType) {
return (compareType == CompareType.AVERAGE ? getAvgTime() : getMinTime());
}
long getMinTime() {
if (minTime <= 2)
return 2;
else
return minTime;
}
public String getMinTimeAsHTML() {
return "MIN TIME "
+ minTime
+ " ms ("
+ df.format((double) minTime / 1000)
+ " sec)
";
}
void setMinTime(long minTime) {
this.minTime = minTime;
}
long getAvgTime() {
return avgTime;
}
public String getAvgTimeAsHTML() {
return "AVERAGE TIME "
+ avgTime
+ " ms ("
+ df.format((double) avgTime / 1000)
+ " sec)
";
}
void setAvgTime(long avgTime) {
this.avgTime = avgTime;
}
public long getNumRuns() {
return numRuns;
}
public String getNumRunsAsHTML() {
return "NUMBER OF RUNS "
+ numRuns + "
";
}
public void setNumRuns(long numRuns) {
this.numRuns = numRuns;
}
public String getQuery() {
return query;
}
public String getQueryAsHTML() {
return "
QUERY "
+ query.replace("'", "") + " (TENANT ID: " + getTenantId()
+ ")
";
}
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 "" + label
+ "
";
}
public void setLabel(String label) {
this.label = label;
}
public long getRowCount() {
return rowCount;
}
public String getRowCountAsHTML() {
return "RESULT ROW COUNT "
+ rowCount + "
";
}
public void setRowCount(long rowCount) {
this.rowCount = rowCount;
}
}
static class StaticGoogleChartsRenderingData {
public static String HEADER = "[title] "
+ ""
+ "PHERFED [title]
[summary]";
}
}