private void adjustMetrics(
      Map<String, Map<Integer, MetricSnapshot>> metrics,
      Map<String, Integer> metaCounters,
      Map<String, Map<Integer, Histogram>> histograms,
      Map<String, Map<Integer, Timer>> timers) {
    for (Map.Entry<String, Map<Integer, MetricSnapshot>> metricEntry : metrics.entrySet()) {
      String meta = metricEntry.getKey();
      MetricType metricType = MetricUtils.metricType(meta);
      MetaType metaType = MetricUtils.metaType(meta);
      Map<Integer, MetricSnapshot> winData = metricEntry.getValue();

      if (metricType == MetricType.HISTOGRAM) {
        for (Map.Entry<Integer, MetricSnapshot> dataEntry : winData.entrySet()) {
          MetricSnapshot snapshot = dataEntry.getValue();
          Integer cnt = metaCounters.get(meta);
          Histogram histogram = histograms.get(meta).get(dataEntry.getKey());
          if (cnt != null && cnt > 1) {

            Snapshot snapshot1 = histogram.getSnapshot();
            snapshot.set_mean(snapshot1.getMean());
            snapshot.set_p50(snapshot1.getMedian());
            snapshot.set_p75(snapshot1.get75thPercentile());
            snapshot.set_p95(snapshot1.get95thPercentile());
            snapshot.set_p98(snapshot1.get98thPercentile());
            snapshot.set_p99(snapshot1.get99thPercentile());
            snapshot.set_p999(snapshot1.get999thPercentile());
            snapshot.set_stddev(snapshot1.getStdDev());
            snapshot.set_min(snapshot1.getMin());
            snapshot.set_max(snapshot1.getMax());

            if (metaType == MetaType.TOPOLOGY) {
              snapshot.set_points(Arrays.asList(ArrayUtils.toObject(snapshot1.getValues())));
            }
          }
          if (metaType != MetaType.TOPOLOGY) {
            snapshot.set_points(new ArrayList<Long>(0));
          }
        }

      } else if (metricType == MetricType.TIMER) {
        for (Map.Entry<Integer, MetricSnapshot> dataEntry : winData.entrySet()) {
          MetricSnapshot snapshot = dataEntry.getValue();
          Integer cnt = metaCounters.get(meta);
          if (cnt != null && cnt > 1) {
            Timer timer = timers.get(meta).get(dataEntry.getKey());
            Snapshot snapshot1 = timer.getSnapshot();
            snapshot.set_p50(snapshot1.getMedian());
            snapshot.set_p75(snapshot1.get75thPercentile());
            snapshot.set_p95(snapshot1.get95thPercentile());
            snapshot.set_p98(snapshot1.get98thPercentile());
            snapshot.set_p99(snapshot1.get99thPercentile());
            snapshot.set_p999(snapshot1.get999thPercentile());
            snapshot.set_stddev(snapshot1.getStdDev());
            snapshot.set_min(snapshot1.getMin());
            snapshot.set_max(snapshot1.getMax());
          }
          snapshot.set_points(new ArrayList<Long>(0));
        }
      }
    }
  }
  public TopologyMetric mergeMetrics() {
    long start = System.currentTimeMillis();

    if (getMemCache().size() == 0) {
      // LOG.info("topology:{}, metric size is 0, skip...", topologyId);
      return null;
    }
    if (isMerging()) {
      LOG.info("topology {} is already merging, skip...", topologyId);
      return null;
    }

    setMerging(true);

    try {
      Map<String, MetricInfo> workerMetricMap = this.memCache;
      // reset mem cache
      this.memCache = new ConcurrentHashMap<>();

      MetricInfo topologyMetrics = MetricUtils.mkMetricInfo();
      MetricInfo componentMetrics = MetricUtils.mkMetricInfo();
      MetricInfo taskMetrics = MetricUtils.mkMetricInfo();
      MetricInfo streamMetrics = MetricUtils.mkMetricInfo();
      MetricInfo workerMetrics = MetricUtils.mkMetricInfo();
      MetricInfo nettyMetrics = MetricUtils.mkMetricInfo();
      TopologyMetric tpMetric =
          new TopologyMetric(
              topologyMetrics,
              componentMetrics,
              workerMetrics,
              taskMetrics,
              streamMetrics,
              nettyMetrics);

      // metric name => worker count
      Map<String, Integer> metricNameCounters = new HashMap<>();

      // special for histograms & timers, we merge the points to get a new snapshot data.
      Map<String, Map<Integer, Histogram>> histograms = new HashMap<>();
      Map<String, Map<Integer, Timer>> timers = new HashMap<>();

      // iterate metrics of all workers within the same topology
      for (ConcurrentMap.Entry<String, MetricInfo> metricEntry : workerMetricMap.entrySet()) {
        MetricInfo metricInfo = metricEntry.getValue();

        // merge counters: add old and new values, note we only add incoming new metrics and
        // overwrite
        // existing data, same for all below.
        Map<String, Map<Integer, MetricSnapshot>> metrics = metricInfo.get_metrics();
        for (Map.Entry<String, Map<Integer, MetricSnapshot>> metric : metrics.entrySet()) {
          String metricName = metric.getKey();
          Map<Integer, MetricSnapshot> data = metric.getValue();
          MetaType metaType = MetricUtils.metaType(metricName);

          MetricType metricType = MetricUtils.metricType(metricName);
          if (metricType == MetricType.COUNTER) {
            mergeCounters(tpMetric, metaType, metricName, data);
          } else if (metricType == MetricType.GAUGE) {
            mergeGauges(tpMetric, metaType, metricName, data);
          } else if (metricType == MetricType.METER) {
            mergeMeters(
                getMetricInfoByType(tpMetric, metaType), metricName, data, metricNameCounters);
          } else if (metricType == MetricType.HISTOGRAM) {
            mergeHistograms(
                getMetricInfoByType(tpMetric, metaType),
                metricName,
                data,
                metricNameCounters,
                histograms);
          } else if (metricType == MetricType.TIMER) {
            mergeTimers(
                getMetricInfoByType(tpMetric, metaType),
                metricName,
                data,
                metricNameCounters,
                timers);
          }
        }
      }
      adjustHistogramTimerMetrics(tpMetric, metricNameCounters, histograms, timers);
      // for counters, we only report delta data every time, need to sum with old data
      // adjustCounterMetrics(tpMetric, oldTpMetric);

      LOG.info(
          "merge topology metrics:{}, cost:{}", topologyId, System.currentTimeMillis() - start);
      // debug logs
      // MetricUtils.printMetricWinSize(componentMetrics);

      return tpMetric;
    } finally {
      setMerging(false);
    }
  }