blob: f622b89f428b22968b260be064e4fd59cc004662 [file] [log] [blame]
/*
* 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.kylin.rest.service;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.kylin.cube.CubeInstance;
import org.apache.kylin.metadata.project.ProjectInstance;
import org.apache.kylin.metadata.project.RealizationEntry;
import org.apache.kylin.metadata.realization.RealizationType;
import org.apache.kylin.metrics.MetricsManager;
import org.apache.kylin.metrics.lib.impl.TimePropertyEnum;
import org.apache.kylin.metrics.property.JobPropertyEnum;
import org.apache.kylin.metrics.property.QueryPropertyEnum;
import org.apache.kylin.rest.constant.Constant;
import org.apache.kylin.rest.exception.BadRequestException;
import org.apache.kylin.rest.request.PrepareSqlRequest;
import org.apache.kylin.rest.response.MetricsResponse;
import org.apache.kylin.rest.response.SQLResponse;
import org.apache.kylin.storage.hybrid.HybridInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component;
import org.apache.kylin.shaded.com.google.common.base.Strings;
import org.apache.kylin.shaded.com.google.common.collect.Lists;
@Component("dashboardService")
public class DashboardService extends BasicService {
private static final Logger logger = LoggerFactory.getLogger(DashboardService.class);
@Autowired
private CubeService cubeService;
public MetricsResponse getCubeMetrics(String projectName, String cubeName) {
MetricsResponse cubeMetrics = new MetricsResponse();
Float totalCubeSize = 0f;
long totalRecoadSize = 0;
List<CubeInstance> cubeInstances = cubeService.listAllCubes(cubeName, projectName, null, true);
Integer totalCube = cubeInstances.size();
if (projectName == null) {
totalCube += getHybridManager().listHybridInstances().size();
} else {
ProjectInstance project = getProjectManager().getProject(projectName);
totalCube += project.getRealizationCount(RealizationType.HYBRID);
}
Float minCubeExpansion = Float.POSITIVE_INFINITY;
Float maxCubeExpansion = Float.NEGATIVE_INFINITY;
cubeMetrics.increase("totalCube", totalCube.floatValue());
for (CubeInstance cubeInstance : cubeInstances) {
if (cubeInstance.getInputRecordSizeBytes() > 0) {
totalCubeSize += cubeInstance.getSizeKB();
totalRecoadSize += cubeInstance.getInputRecordSizeBytes();
Float cubeExpansion = new Float(cubeInstance.getSizeKB()) * 1024
/ cubeInstance.getInputRecordSizeBytes();
if (cubeExpansion > maxCubeExpansion) {
maxCubeExpansion = cubeExpansion;
}
if (cubeExpansion < minCubeExpansion) {
minCubeExpansion = cubeExpansion;
}
}
}
Float avgCubeExpansion = 0f;
if (totalRecoadSize != 0) {
avgCubeExpansion = totalCubeSize * 1024 / totalRecoadSize;
}
cubeMetrics.increase("avgCubeExpansion", avgCubeExpansion);
cubeMetrics.increase("maxCubeExpansion", maxCubeExpansion == Float.NEGATIVE_INFINITY ? 0 : maxCubeExpansion);
cubeMetrics.increase("minCubeExpansion", minCubeExpansion == Float.POSITIVE_INFINITY ? 0 : minCubeExpansion);
return cubeMetrics;
}
private List<CubeInstance> getCubeByHybrid(HybridInstance hybridInstance) {
List<CubeInstance> cubeInstances = Lists.newArrayList();
List<RealizationEntry> realizationEntries = hybridInstance.getRealizationEntries();
for (RealizationEntry realizationEntry : realizationEntries) {
String reName = realizationEntry.getRealization();
if (RealizationType.CUBE == realizationEntry.getType()) {
CubeInstance cubeInstance = getCubeManager().getCube(reName);
cubeInstances.add(cubeInstance);
} else if (RealizationType.HYBRID == realizationEntry.getType()) {
HybridInstance innerHybridInstance = getHybridManager().getHybridInstance(reName);
cubeInstances.addAll(getCubeByHybrid(innerHybridInstance));
}
}
return cubeInstances;
};
public PrepareSqlRequest getQueryMetricsSQLRequest(String startTime, String endTime, String projectName,
String cubeName) {
String[] metrics = new String[] { QueryMetricEnum.QUERY_COUNT.toSQL(),
QueryMetricEnum.AVG_QUERY_LATENCY.toSQL(), QueryMetricEnum.MAX_QUERY_LATENCY.toSQL(),
QueryMetricEnum.MIN_QUERY_LATENCY.toSQL() };
Map<String, String> filterMap = getBaseFilterMap(CategoryEnum.QUERY, projectName, startTime, endTime);
filterMap.putAll(getCubeFilterMap(CategoryEnum.QUERY, cubeName));
return createPrepareSqlRequest(null, metrics,
getMetricsManager().getSystemTableFromSubject(getConfig().getKylinMetricsSubjectQuery()), filterMap);
};
public PrepareSqlRequest getJobMetricsSQLRequest(String startTime, String endTime, String projectName,
String cubeName) {
String[] metrics = new String[] { JobMetricEnum.JOB_COUNT.toSQL(), JobMetricEnum.AVG_JOB_BUILD_TIME.toSQL(),
JobMetricEnum.MAX_JOB_BUILD_TIME.toSQL(), JobMetricEnum.MIN_JOB_BUILD_TIME.toSQL() };
Map<String, String> filterMap = getBaseFilterMap(CategoryEnum.JOB, projectName, startTime, endTime);
filterMap.putAll(getCubeFilterMap(CategoryEnum.JOB, cubeName));
return createPrepareSqlRequest(null, metrics,
getMetricsManager().getSystemTableFromSubject(getConfig().getKylinMetricsSubjectJob()), filterMap);
}
public PrepareSqlRequest getChartSQLRequest(String startTime, String endTime, String projectName, String cubeName,
String dimension, String metric, String category) {
try {
CategoryEnum categoryEnum = CategoryEnum.valueOf(category);
String table = "";
String[] dimensionSQL = null;
String[] metricSQL = null;
if (categoryEnum == CategoryEnum.QUERY) {
dimensionSQL = new String[] { QueryDimensionEnum.valueOf(dimension).toSQL() };
metricSQL = new String[] { QueryMetricEnum.valueOf(metric).toSQL() };
table = getMetricsManager().getSystemTableFromSubject(getConfig().getKylinMetricsSubjectQuery());
} else if (categoryEnum == CategoryEnum.JOB) {
dimensionSQL = new String[] { JobDimensionEnum.valueOf(dimension).toSQL() };
metricSQL = new String[] { JobMetricEnum.valueOf(metric).toSQL() };
table = getMetricsManager().getSystemTableFromSubject(getConfig().getKylinMetricsSubjectJob());
}
Map<String, String> filterMap = getBaseFilterMap(categoryEnum, projectName, startTime, endTime);
filterMap.putAll(getCubeFilterMap(categoryEnum, cubeName));
return createPrepareSqlRequest(dimensionSQL, metricSQL, table, filterMap);
} catch (IllegalArgumentException e) {
String message = "Generate dashboard chart sql failed. Please double check the input parameter: dimension, metric or category.";
logger.error(message, e);
throw new BadRequestException(message + " Caused by: " + e.getMessage(), null, e.getCause());
}
}
public MetricsResponse transformChartData(SQLResponse sqlResponse) {
if (!sqlResponse.getIsException()) {
MetricsResponse metrics = new MetricsResponse();
List<List<String>> results = sqlResponse.getResults();
for (List<String> result : results) {
String dimension = result.get(0);
if (dimension != null && !dimension.isEmpty()) {
String metric = result.get(1);
metrics.increase(dimension, getMetricValue(metric));
}
}
return metrics;
}
return null;
}
public Float getMetricValue(String value) {
if (value == null || value.isEmpty()) {
return 0f;
} else {
return Float.valueOf(value);
}
}
@PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN + " or hasPermission(#project, 'ADMINISTRATION')")
public void checkAuthorization(ProjectInstance project) throws AccessDeniedException {
}
@PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN)
public void checkAuthorization() throws AccessDeniedException {
}
private Map<String, String> getBaseFilterMap(CategoryEnum category, String projectName, String startTime,
String endTime) {
HashMap<String, String> filterMap = new HashMap<>();
String project = "";
if (category == CategoryEnum.QUERY) {
project = QueryDimensionEnum.PROJECT.toSQL();
} else {
project = JobDimensionEnum.PROJECT.toSQL();
}
filterMap.put(TimePropertyEnum.DAY_DATE.toString() + " >= ?", startTime);
filterMap.put(TimePropertyEnum.DAY_DATE.toString() + " <= ?", endTime);
if (!Strings.isNullOrEmpty(projectName)) {
filterMap.put(project + " = ?", projectName.toUpperCase(Locale.ROOT));
} else {
filterMap.put(project + " <> ?", MetricsManager.SYSTEM_PROJECT);
}
return filterMap;
}
private Map<String, String> getCubeFilterMap(CategoryEnum category, String cubeName) {
HashMap<String, String> filterMap = new HashMap<>();
if (category == CategoryEnum.QUERY) {
filterMap.put(QueryPropertyEnum.EXCEPTION.toString() + " = ?", "NULL");
if (!Strings.isNullOrEmpty(cubeName)) {
filterMap.put(QueryPropertyEnum.REALIZATION + " = ?", cubeName);
}
} else if (category == CategoryEnum.JOB && !Strings.isNullOrEmpty(cubeName)) {
HybridInstance hybridInstance = getHybridManager().getHybridInstance(cubeName);
if (null != hybridInstance) {
StringBuffer cubeNames = new StringBuffer();
for (CubeInstance cube : getCubeByHybrid(hybridInstance)) {
cubeNames.append(",'" + cube.getName() + "'");
}
filterMap.put(JobPropertyEnum.CUBE.toString() + " IN (?)", cubeNames.substring(1));
} else {
filterMap.put(JobPropertyEnum.CUBE.toString() + " = ?", cubeName);
}
}
return filterMap;
}
private PrepareSqlRequest createPrepareSqlRequest(String[] dimensions, String[] metrics, String category,
Map<String, String> filterMap) {
PrepareSqlRequest sqlRequest = new PrepareSqlRequest();
sqlRequest.setProject(MetricsManager.SYSTEM_PROJECT);
StringBuffer baseSQL = new StringBuffer("select ");
StringBuffer groupBy = new StringBuffer("");
if (dimensions != null && dimensions.length > 0) {
groupBy.append(" group by ");
StringBuffer dimensionSQL = new StringBuffer("");
for (String dimension : dimensions) {
dimensionSQL.append(",");
dimensionSQL.append(dimension);
}
baseSQL.append(dimensionSQL.substring(1));
groupBy.append(dimensionSQL.substring(1));
}
if (metrics != null && metrics.length > 0) {
StringBuffer metricSQL = new StringBuffer("");
for (String metric : metrics) {
metricSQL.append(",");
metricSQL.append(metric);
}
if (groupBy.length() > 0) {
baseSQL.append(metricSQL);
} else {
baseSQL.append(metricSQL.substring(1));
}
}
baseSQL.append(" from ");
baseSQL.append(category);
if (filterMap != null && filterMap.size() > 0) {
PrepareSqlRequest.StateParam[] params = new PrepareSqlRequest.StateParam[filterMap.size()];
int i = 0;
StringBuffer filterSQL = new StringBuffer(" where ");
Iterator<String> it = filterMap.keySet().iterator();
String filter = it.next();
filterSQL.append(filter);
params[i] = new PrepareSqlRequest.StateParam();
params[i].setClassName("java.lang.String");
params[i++].setValue(filterMap.get(filter));
while (it.hasNext()) {
filter = it.next();
filterSQL.append(" and ");
filterSQL.append(filter);
params[i] = new PrepareSqlRequest.StateParam();
params[i].setClassName("java.lang.String");
params[i++].setValue(filterMap.get(filter));
}
baseSQL.append(filterSQL.toString());
sqlRequest.setParams(params);
}
baseSQL.append(groupBy);
sqlRequest.setSql(baseSQL.toString());
return sqlRequest;
}
private enum CategoryEnum {
QUERY, JOB
}
private enum QueryDimensionEnum {
PROJECT(QueryPropertyEnum.PROJECT.toString()), //
CUBE(QueryPropertyEnum.REALIZATION.toString()), //
DAY(TimePropertyEnum.DAY_DATE.toString()), //
WEEK(TimePropertyEnum.WEEK_BEGIN_DATE.toString()), //
MONTH(TimePropertyEnum.MONTH.toString());
private final String sql;
QueryDimensionEnum(String sql) {
this.sql = sql;
}
public String toSQL() {
return this.sql;
}
}
private enum JobDimensionEnum {
PROJECT(JobPropertyEnum.PROJECT.toString()), //
CUBE(JobPropertyEnum.CUBE.toString()), //
DAY(TimePropertyEnum.DAY_DATE.toString()), //
WEEK(TimePropertyEnum.WEEK_BEGIN_DATE.toString()), //
MONTH(TimePropertyEnum.MONTH.toString());
private final String sql;
JobDimensionEnum(String sql) {
this.sql = sql;
}
public String toSQL() {
return this.sql;
}
}
private enum QueryMetricEnum {
QUERY_COUNT("count(*)"), //
AVG_QUERY_LATENCY("avg(" + QueryPropertyEnum.TIME_COST.toString() + ")"), //
MAX_QUERY_LATENCY("max(" + QueryPropertyEnum.TIME_COST.toString() + ")"), //
MIN_QUERY_LATENCY("min(" + QueryPropertyEnum.TIME_COST.toString() + ")");
private final String sql;
QueryMetricEnum(String sql) {
this.sql = sql;
}
public String toSQL() {
return this.sql;
}
}
private enum JobMetricEnum {
JOB_COUNT("count(*)"), //
AVG_JOB_BUILD_TIME("avg(" + JobPropertyEnum.PER_BYTES_TIME_COST.toString() + ")"), //
MAX_JOB_BUILD_TIME("max(" + JobPropertyEnum.PER_BYTES_TIME_COST.toString() + ")"), //
MIN_JOB_BUILD_TIME("min(" + JobPropertyEnum.PER_BYTES_TIME_COST.toString() + ")");
private final String sql;
JobMetricEnum(String sql) {
this.sql = sql;
}
public String toSQL() {
return this.sql;
}
}
}