public synchronized void gatherSeries(long milliStart, long milliEnd) {
    ArrayList<Datapoint> pre, range, post;
    boolean has_data;
    long oldAggregationMs = mAggregationMs;

    mQueryStart = milliStart;
    mQueryEnd = milliEnd;

    setCollectionTimes(milliStart, milliEnd);
    for (int i = 0; i < mSeries.size(); i++) {
      has_data = false;

      TimeSeries ts = mSeries.get(i);
      if (ts == null || ts.getDbRow().getSynthetic()) continue;

      if (ts.isEnabled() == false) {
        boolean skip = true;
        for (int j = 0; j < ts.getDependees().size(); j++) {
          if (ts.getDependees().get(j).isEnabled() == true) {
            skip = false;
            break;
          }
        }
        if (skip == true) continue;
      }

      mDatapointCache.populateRange(
          ts.getDbRow().getId(), mCollectionStart, mCollectionEnd, mAggregationMs);

      pre = mDatapointCache.getDataBefore(ts.getDbRow().getId(), mHistory, mCollectionStart);
      if (pre != null && pre.size() > 0) has_data = true;

      range =
          mDatapointCache.getDataInRange(ts.getDbRow().getId(), mCollectionStart, mCollectionEnd);
      if (range != null && range.size() > 0) has_data = true;

      post = mDatapointCache.getDataAfter(ts.getDbRow().getId(), 1, mCollectionEnd);
      if (post != null && range.size() > 0) has_data = true;

      if (has_data == true) ts.setDatapoints(pre, range, post, true);
    }

    generateSynthetics();

    ArrayList<TimeSeries> enabledSeries = getAllEnabledSeries();
    for (int i = 0; i < enabledSeries.size(); i++) {
      aggregateDatapoints(enabledSeries.get(i));
    }

    mAggregationMs = oldAggregationMs;

    return;
  }