code: private List<String> constructMetricQueryKeys(MetricQuery query) {
          Long startTimeQuery = query.getStartTimestamp();
          Long endTimeQuery = query.getEndTimestamp();
          List<String> metricQueryKeys = new ArrayList<String>();
  
          for (Long timeStamp = startTimeQuery; timeStamp < endTimeQuery; timeStamp = timeStamp + DURATION_IN_MILLIS) {
              StringBuilder sb = new StringBuilder();
  
              sb.append(timeStamp).append(":");
              sb.append(query.getNamespace()).append(":");
              sb.append(query.getScope()).append(":");
              sb.append(query.getMetric()).append(":");
  
              // sort the tag key and values within each tag key alphabetically
              Map<String, String> treeMap = new TreeMap<String, String>();
  
              for (Map.Entry<String, String> tag : query.getTags().entrySet()) {
                  String[] tagValues = tag.getValue().split("\\|");
  
                  Arrays.sort(tagValues);
  
                  StringBuilder sbTag = new StringBuilder();
                  String separator = "";
  
                  for (String tagValue : tagValues) {
                      sbTag.append(separator);
                      separator = "|";
                      sbTag.append(tagValue);
                  }
                  treeMap.put(tag.getKey(), sbTag.toString());
              }
              sb.append(treeMap).append(":");
              sb.append(query.getAggregator()).append(":");
              sb.append(query.getDownsampler()).append(":");
              sb.append(query.getDownsamplingPeriod());
              metricQueryKeys.add(sb.toString());
          }
          return metricQueryKeys;
      }
docs: Constructs a cache key from start time stamp boundary, and tags in metric query.
  
  @param   query                   The query to use to construct the cache key.
  
  @return  The cache key.