in services/metric-service/src/main/java/com/amazon/aws/partners/saasfactory/saasboost/MetricServiceDAL.java [105:272]
public List<QueryResult> queryMetrics(final MetricQuery query) throws Exception {
long startTimeMillis = System.currentTimeMillis();
LOGGER.info("queryMetrics: start");
List<MetricResultItem> listResult = new ArrayList<>();
List<String> periodList = new ArrayList<>();
List<QueryResult> queryResultList = new ArrayList<>();
QueryResult mrs = new QueryResult();
mrs.setId(query.getId());
try {
List<String> tenants;
if (!query.getTenants().isEmpty()) {
tenants = new ArrayList<>();
tenants.addAll((query.getTenants()));
LOGGER.debug(("queryMetrics: use tenants from query"));
} else {
tenants = getTenants();
}
if (tenants.size() > 500) {;
throw new RuntimeException("queryMetrics: Cannot process more than 500 tenants");
} else if (tenants.isEmpty()) {
throw new RuntimeException("queryMetrics: No tenants to process");
}
//build query
final List<MetricDataQuery> dq = buildCWDataQuery(query, tenants);
//now that query is built let's execute and get resultant data
//the data will be stored in Metric object and placed in map by MetricDimension.
Map<MetricDimension, Metric> metricMap = loadCWMetricData(query, dq);
LOGGER.debug("queryMetrics: metricMap item count: " + metricMap.size());
for (final Map.Entry<MetricDimension, Metric> metricEntry : metricMap.entrySet()) {
final Metric metric = metricEntry.getValue();
final MetricDimension metricDimension = metricEntry.getKey();
LOGGER.debug("queryMetrics: Dimension: " + metricDimension.toString() + " Metric count: " + metric.getMetricValues().size());
//construct a MetricDimension without a Tenant Id as the metrics are not by tenant
MetricDimension md = new MetricDimension(metricDimension.getNameSpace(), metricDimension.getMetricName(), null);
MetricResultItem mr = new MetricResultItem();
mr.setDimension(md);
List<Double> p90List = new ArrayList<>();
List<Double> p70List = new ArrayList<>();
List<Double> p50List = new ArrayList<>();
List<Double> avgList = new ArrayList<>();
List<Double> sumList = new ArrayList<>();
Map<String, Double> tenantSumMap = new LinkedHashMap<>();
//now process the metric entries
for (Map.Entry<Instant, PriorityQueue<MetricValue>> entry : metric.getTimeValMap().entrySet()) {
//add entry for the period key
// LOGGER.debug("queryMetrics: Period: " + entry.getKey());
final String period = formatter.format(Date.from(entry.getKey()));
if (!periodList.contains(period)) {
periodList.add(period);
}
//build array list in order of least to high
final List<MetricValue> metricValueList = new ArrayList<>(entry.getValue().size());
while (!entry.getValue().isEmpty()) {
//poll retrieve least item first
MetricValue tenantVal = entry.getValue().poll();
metricValueList.add(tenantVal);
if (query.isTopTenants()) {
//Note: The -1 is to get the reverse order from greatest to least when we later sort
if (tenantSumMap.containsKey(tenantVal.getId())) {
tenantSumMap.put(tenantVal.getId(), tenantSumMap.get(tenantVal.getId()) - tenantVal.getValue());
} else {
tenantSumMap.put(tenantVal.getId(), -1 * tenantVal.getValue());
}
}
}
//*TODO: Maybe get rid of this as we only care for entire period
//get top 10 from list starting at last item in Array index.
/* if (query.isTopTenants()) {
int leastPos = 0;
if (metricValueList.size() > 10) {
leastPos = metricValueList.size() - 10;
}
//LOGGER.debug(("size: " + metricValueList.size() + ", offset: " + leastPos));
List<MetricValue> topTenantListForPeriod = new ArrayList<>();
for (int i = metricValueList.size() - 1; i >= leastPos; i--) {
topTenantListForPeriod.add(metricValueList.get(i));
}
mr.addTopTenant(topTenantListForPeriod);
}*/
if (query.isStatsMap()) {
final Map<String, Double> percentilesMap = MetricHelper.getPercentiles(metricValueList);
p90List.add(percentilesMap.get("p90"));
p70List.add(percentilesMap.get("p70"));
p50List.add(percentilesMap.get("p50"));
avgList.add(percentilesMap.get("Average"));
sumList.add(percentilesMap.get("Sum"));
}
}
//let's store the lists
if (query.isStatsMap()) {
mr.putStat("P90", p90List);
mr.putStat("P70", p70List);
mr.putStat("P50", p50List);
mr.putStat("Average", avgList);
mr.putStat("Sum", sumList);
}
//now compute the top 10 tenants
if (query.isTopTenants()) {
Map<String, Double> sortedTenantValMap = tenantSumMap.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldValue, newValue) -> oldValue, LinkedHashMap::new));
//now iterate through and get top 10.
List<MetricValue> topTenantList = new ArrayList<>();
int i=0;
for (Map.Entry<String, Double> entry : sortedTenantValMap.entrySet()) {
//if the stat is average then divide by number of periods.
MetricValue mv = new MetricValue(-1 * entry.getValue(), entry.getKey());
if ("Average".equalsIgnoreCase(query.getStat())) {
BigDecimal bd = new BigDecimal(-1 * entry.getValue() / periodList.size()).setScale(3, RoundingMode.HALF_UP);
mv.setValue(bd.doubleValue());
}
topTenantList.add(mv);
i++;
//only output first 10
if (10 == i) {
break;
}
}
//**TODO: now we have to reverse the order.
mr.setTopTenant(topTenantList);
}
listResult.add(mr);
}
if (query.isTenantTaskMaxCapacity()) {
Map<String, Integer> tenantTaskMaxCapacityMap = this.getTaskMaxCapacity(tenants);
//build array of metric value to return
List<MetricValue> tenantTaskMaxCapacityList = new ArrayList<>();
for (Map.Entry<String, Integer> entry : tenantTaskMaxCapacityMap.entrySet()) {
MetricValue mv = new MetricValue(entry.getValue(), entry.getKey());
tenantTaskMaxCapacityList.add(mv);
}
mrs.setTenantTaskMaxCapacity(tenantTaskMaxCapacityList);
}
mrs.setMetrics(listResult);
mrs.setPeriods(periodList);
queryResultList.add(mrs);
} catch (CloudWatchException e) {
LOGGER.error("queryMetrics: " + e.awsErrorDetails().errorMessage());
LOGGER.error("queryMetrics: ", e);
LOGGER.error(Utils.getFullStackTrace(e));
throw e;
}
long totalTimeMillis = System.currentTimeMillis() - startTimeMillis;
LOGGER.info("queryMetrics: exec " + totalTimeMillis);
return queryResultList;
//return mrs;
}