monasca-api/java/src/main/java/monasca/api/infrastructure/persistence/vertica/MetricQueries.java

295 lines
9.1 KiB
Java

/*
* (C) Copyright 2014,2016 Hewlett Packard Enterprise Development LP
*
* Licensed 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 monasca.api.infrastructure.persistence.vertica;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.joda.time.DateTime;
import org.skife.jdbi.v2.Handle;
import org.skife.jdbi.v2.Query;
import monasca.api.domain.exception.MultipleMetricsException;
import monasca.api.domain.model.measurement.Measurements;
/**
* Vertica utilities for building metric queries.
*/
final class MetricQueries {
private static final Splitter BAR_SPLITTER = Splitter.on('|').omitEmptyStrings().trimResults();
private static final char OFFSET_SEPARATOR = '_';
private static final Splitter offsetSplitter = Splitter.on(OFFSET_SEPARATOR).omitEmptyStrings().trimResults();
static final String FIND_METRIC_DEFS_SQL =
"SELECT %s TO_HEX(defDims.id) as defDimsId, def.name, dims.name as dName, dims.value AS dValue "
+ "FROM MonMetrics.Definitions def "
+ "JOIN MonMetrics.DefinitionDimensions defDims ON def.id = defDims.definition_id "
// Outer join needed in case there are no dimensions for a definition.
+ "LEFT OUTER JOIN MonMetrics.Dimensions dims ON dims.dimension_set_id = defDims"
+ ".dimension_set_id "
+ "WHERE TO_HEX(defDims.id) in (%s) "
+ "ORDER BY defDims.id ASC";
static final String METRIC_DEF_SUB_SQL =
"SELECT TO_HEX(defDimsSub.id) as id "
+ "FROM MonMetrics.Definitions as defSub "
+ "JOIN MonMetrics.DefinitionDimensions as defDimsSub ON defDimsSub.definition_id = defSub.id "
+ "%s " // possible measurements time join here
+ "WHERE defSub.tenant_id = :tenantId "
+ "%s " // metric name here
+ "%s " // dimension and clause here
+ "%s " // possible time and clause here
+ "GROUP BY defDimsSub.id";
private static final String MEASUREMENT_AND_CLAUSE =
"AND time_stamp >= :startTime "; // start or start and end time here
private static final String MEASUREMENT_JOIN =
"JOIN MonMetrics.Measurements AS meas ON defDimsSub.id = meas.definition_dimensions_id";
private static final String TABLE_TO_JOIN_ON = "defDimsSub";
private MetricQueries() {}
static String buildMetricDefinitionSubSql(String name, Map<String, String> dimensions,
DateTime startTime, DateTime endTime) {
String namePart = "";
if (name != null && !name.isEmpty()) {
namePart = "AND defSub.name = :name ";
}
return String.format(METRIC_DEF_SUB_SQL,
buildTimeJoin(startTime),
namePart,
buildDimensionAndClause(dimensions, TABLE_TO_JOIN_ON),
buildTimeAndClause(startTime, endTime));
}
static String buildDimensionAndClause(Map<String, String> dimensions,
String tableToJoinName) {
if (dimensions == null || dimensions.isEmpty()) {
return "";
}
StringBuilder sb = new StringBuilder();
sb.append(" and ").append(tableToJoinName).append(
".id in ( "
+ "SELECT defDimsSub2.id FROM MonMetrics.Dimensions AS dimSub " +
"JOIN MonMetrics.DefinitionDimensions AS defDimsSub2 " +
"ON defDimsSub2.dimension_set_id = dimSub.dimension_set_id" +
" WHERE (");
int i = 0;
for (Iterator<Map.Entry<String, String>> it = dimensions.entrySet().iterator(); it.hasNext(); i++) {
Map.Entry<String, String> entry = it.next();
sb.append("(name = :dname").append(i);
String dim_value = entry.getValue();
if (!Strings.isNullOrEmpty(dim_value)) {
List<String> values = BAR_SPLITTER.splitToList(dim_value);
if (values.size() > 1) {
sb.append(" and ( ");
for (int j = 0; j < values.size(); j++) {
sb.append("value = :dvalue").append(i).append('_').append(j);
if (j < values.size() - 1) {
sb.append(" or ");
}
}
sb.append(")");
} else {
sb.append(" and value = :dvalue").append(i);
}
}
sb.append(")");
if (it.hasNext()) {
sb.append(" or ");
}
}
sb.append(") GROUP BY defDimsSub2.id,dimSub.dimension_set_id HAVING count(*) = ").append(dimensions.size()).append(") ");
return sb.toString();
}
static String buildTimeAndClause(
DateTime startTime,
DateTime endTime)
{
if (startTime == null) {
return "";
}
StringBuilder timeAndClause = new StringBuilder();
timeAndClause.append(MEASUREMENT_AND_CLAUSE);
if (endTime != null) {
timeAndClause.append("AND time_stamp <= :endTime ");
}
return timeAndClause.toString();
}
static String buildTimeJoin(DateTime startTime)
{
if (startTime == null) {
return "";
}
return MEASUREMENT_JOIN;
}
static void bindDimensionsToQuery(Query<?> query, Map<String, String> dimensions) {
if (dimensions != null) {
int i = 0;
for (Iterator<Map.Entry<String, String>> it = dimensions.entrySet().iterator(); it.hasNext(); i++) {
Map.Entry<String, String> entry = it.next();
query.bind("dname" + i, entry.getKey());
if (!Strings.isNullOrEmpty(entry.getValue())) {
List<String> values = BAR_SPLITTER.splitToList(entry.getValue());
if (values.size() > 1) {
for (int j = 0; j < values.size(); j++) {
query.bind("dvalue" + i + '_' + j, values.get(j));
}
}
else {
query.bind("dvalue" + i, entry.getValue());
}
}
}
}
}
static void bindOffsetToQuery(Query<Map<String, Object>> query, String offset) {
List<String> offsets = offsetSplitter.splitToList(offset);
if (offsets.size() > 1) {
query.bind("offset_id", offsets.get(0));
query.bind("offset_timestamp",
new Timestamp(DateTime.parse(offsets.get(1)).getMillis()));
} else {
query.bind("offset_timestamp",
new Timestamp(DateTime.parse(offsets.get(0)).getMillis()));
}
}
static void checkForMultipleDefinitions(Handle h, String tenantId, String name, Map<String, String> dimensions)
throws MultipleMetricsException {
String namePart = "";
if (name != null && !name.isEmpty()) {
namePart = "AND name = :name ";
}
String sql = String.format(METRIC_DEF_SUB_SQL,
"",
namePart,
buildDimensionAndClause(dimensions,
TABLE_TO_JOIN_ON),
"") + " limit 2";
Query<Map<String, Object>> query = h.createQuery(sql);
query.bind("tenantId", tenantId);
if (name != null) {
query.bind("name", name);
}
bindDimensionsToQuery(query, dimensions);
List<Map<String, Object>> rows = query.list();
if (rows.size() > 1) {
throw new MultipleMetricsException(name, dimensions);
}
}
static void addDefsToResults(Map<String, ? extends Measurements> results, Handle h, String dbHint) {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (String id : results.keySet()) {
if (first) {
sb.append("'").append(id).append("'");
first = false;
} else {
sb.append(',').append("'").append(id).append("'");
}
}
String defDimSql = String.format(MetricQueries.FIND_METRIC_DEFS_SQL,
dbHint,
sb.toString());
Query<Map<String, Object>> query = h.createQuery(defDimSql);
List<Map<String, Object>> rows = query.list();
String currentDefDimId = null;
Map<String, String> dims = null;
for (Map<String, Object> row : rows) {
String defDimId = (String) row.get("defDimsId");
String defName = (String) row.get("name");
String dimName = (String) row.get("dName");
String dimValue = (String) row.get("dValue");
if (defDimId != null && !defDimId.equals(currentDefDimId)) {
currentDefDimId = defDimId;
dims = new HashMap<>();
if (dimName != null && dimValue != null)
dims.put(dimName, dimValue);
results.get(defDimId).setId(defDimId);
results.get(defDimId).setName(defName);
results.get(defDimId).setDimensions(dims);
} else {
if (dimName != null && dimValue != null)
dims.put(dimName, dimValue);
}
}
}
}