docs: public MetricResponse getMetrics(
        MetricRequest request, MetricsFilter metricNameType) {
      LOG.fine("received query: " + request.toString());
      synchronized (CacheCore.class) {
        List<MetricDatum> response = new LinkedList<>();
  
        // candidate metric names
        Set<String> metricNameFilter = request.getMetricNames();
        if (metricNameFilter == null) {
          metricNameFilter = idxMetricName.keySet();
        }
  
        // candidate component names
        Map<String, Set<String>> componentInstanceMap = request.getComponentNameInstanceId();
        Set<String> componentNameFilter;
        if (componentInstanceMap == null) {
          componentNameFilter = idxComponentInstance.keySet();
        } else {
          componentNameFilter = componentInstanceMap.keySet();
        }
  
        for (String metricName : metricNameFilter) {
          if (!metricExists(metricName)) {
            continue;
          }
          MetricsFilter.MetricAggregationType type = metricNameType.getAggregationType(metricName);
          for (String componentName : componentNameFilter) {
            // candidate instance ids
            Set<String> instanceIdFilter;
            if (componentInstanceMap == null
                || componentInstanceMap.get(componentName) == null) {
              instanceIdFilter = idxComponentInstance.get(componentName).keySet();
            } else {
              instanceIdFilter = componentInstanceMap.get(componentName);
            }
  
            for (String instanceId : instanceIdFilter) {
              LOG.fine(componentName + "; " + instanceId + "; " + metricName + "; " + type);
              // get bucket_id
              int idx1 = idxComponentInstance.get(componentName).get(instanceId);
              int idx2 = idxMetricName.get(metricName);
              long bucketId = makeBucketId(idx1, idx2);
  
              // iterate buckets: the result may be empty due to the bucketId/hash filter
              List<MetricTimeRangeValue> metricValue = new LinkedList<>();
              switch (request.getAggregationGranularity()) {
                case AGGREGATE_ALL_METRICS:
                case AGGREGATE_BY_BUCKET:
                  getAggregatedMetrics(metricValue,
                      request.getStartTime()/*when*/, request.getEndTime()/*when*/,
                      bucketId/*where*/, type/*how*/, request.getAggregationGranularity());
                  break;
                case RAW:
                  getRawMetrics(metricValue,
                      request.getStartTime(), request.getEndTime(), bucketId, type);
                  break;
                default:
                  LOG.warning("unknown aggregationGranularity type "
                      + request.getAggregationGranularity());
              }
  
              // make metric list in response
              response.add(new MetricDatum(componentName, instanceId, metricName, metricValue));
            } // end for: instance
          } // end for: component
        } // end for: metric
        return new MetricResponse(response);
      }
    }