public List queryMetrics()

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;
    }