All Downloads are FREE. Search and download functionalities are using the official Maven repository.

co.cask.cdap.test.MetricsManager Maven / Gradle / Ivy

There is a newer version: 5.1.2
Show newest version
/*
 * Copyright © 2015 Cask Data, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package co.cask.cdap.test;

import co.cask.cdap.api.dataset.lib.cube.AggregationFunction;
import co.cask.cdap.api.dataset.lib.cube.TimeValue;
import co.cask.cdap.api.metrics.MetricDataQuery;
import co.cask.cdap.api.metrics.MetricStore;
import co.cask.cdap.api.metrics.MetricTimeSeries;
import co.cask.cdap.api.metrics.RuntimeMetrics;
import co.cask.cdap.common.conf.Constants;
import co.cask.cdap.common.metrics.MetricsTags;
import co.cask.cdap.proto.Id;
import co.cask.cdap.proto.ProgramType;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;
import com.google.common.base.Throwables;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.annotation.Nullable;

/**
 * Used by tests for querying metrics system.
 */
public class MetricsManager {

  private MetricStore metricStore;

  public MetricsManager(MetricStore metricStore) {
    this.metricStore = metricStore;
  }

  /**
   * query the metric store and return the Collection
   * @param query
   * @return Collection
   * @throws Exception
   */
  public Collection query(MetricDataQuery query) throws Exception {
    return metricStore.query(query);
  }

  /**
   * returns flowlet related metrics
   * @param namespace
   * @param appName
   * @param flowName
   * @param flowletName
   * @return {@link co.cask.cdap.api.metrics.RuntimeMetrics}
   */
  public RuntimeMetrics getFlowletMetrics(String namespace, String appName,
                                          String flowName, String flowletName) {

    Id.Program id = Id.Program.from(namespace, appName, ProgramType.FLOW, flowName);
    return getMetrics(MetricsTags.flowlet(id, flowletName),
                      Constants.Metrics.Name.Flow.FLOWLET_INPUT,
                      Constants.Metrics.Name.Flow.FLOWLET_PROCESSED,
                      Constants.Metrics.Name.Flow.FLOWLET_EXCEPTIONS);
  }

  /**
   * returns service related metrics
   * @param namespace
   * @param applicationId
   * @param serviceId
   * @return {@link co.cask.cdap.api.metrics.RuntimeMetrics}
   */
  public RuntimeMetrics getServiceMetrics(String namespace, String applicationId, String serviceId) {
    Id.Program id = Id.Program.from(namespace, applicationId, ProgramType.SERVICE, serviceId);
    return getMetrics(MetricsTags.service(id),
                      Constants.Metrics.Name.Service.SERVICE_INPUT,
                      Constants.Metrics.Name.Service.SERVICE_PROCESSED,
                      Constants.Metrics.Name.Service.SERVICE_EXCEPTIONS);
  }

  /**
   * returns service handler related metrics
   * @param namespace
   * @param applicationId
   * @param serviceId
   * @param handlerId
   * @return {@link co.cask.cdap.api.metrics.RuntimeMetrics}
   */
  public RuntimeMetrics getServiceHandlerMetrics(String namespace, String applicationId, String serviceId,
                                                 String handlerId) {
    Id.Program id = Id.Program.from(namespace, applicationId, ProgramType.SERVICE, serviceId);
    return getMetrics(MetricsTags.serviceHandler(id, handlerId),
                      Constants.Metrics.Name.Service.SERVICE_INPUT,
                      Constants.Metrics.Name.Service.SERVICE_PROCESSED,
                      Constants.Metrics.Name.Service.SERVICE_EXCEPTIONS);
  }


  /**
   * get metrics total count value for a given context and metric.
   * @param tags that identify a context
   * @param metricName
   * @return the total metric
   */
  public long getTotalMetric(Map tags, String metricName) {
    MetricDataQuery query = getTotalCounterQuery(tags, metricName);
    return getSingleValueFromTotals(query);
  }


  /**
   * waitFor a metric value count for the metric identified by metricName and context.
   * @param tags - context identified by tags map
   * @param metricName
   * @param count - expected metric total count value
   * @param timeout
   * @param timeoutUnit
   * @throws TimeoutException
   * @throws InterruptedException
   */
  public void waitForTotalMetricCount(Map tags, String metricName, long count, long timeout,
                                      TimeUnit timeoutUnit) throws TimeoutException, InterruptedException {
    long value = getTotalMetric(tags, metricName);

    // Min sleep time is 10ms, max sleep time is 1 seconds
    long sleepMillis = Math.max(10, Math.min(timeoutUnit.toMillis(timeout) / 10, TimeUnit.SECONDS.toMillis(1)));
    Stopwatch stopwatch = new Stopwatch().start();
    while (value < count && stopwatch.elapsedTime(timeoutUnit) < timeout) {
      TimeUnit.MILLISECONDS.sleep(sleepMillis);
      value = getTotalMetric(tags, metricName);
    }

    if (value < count) {
      throw new TimeoutException("Time limit reached: Expected '" + count + "' but got '" + value + "'");
    }
  }

  /**
   * deletes all metrics
   * @throws Exception
   */
  public void resetAll() throws Exception {
    metricStore.deleteAll();
  }

  private RuntimeMetrics getMetrics(final Map context,
                                    final String inputName,
                                    final String processedName,
                                    @Nullable final String exceptionName) {
    return new RuntimeMetrics() {
      @Override
      public long getInput() {
        return getTotalMetric(context, inputName);
      }

      @Override
      public long getProcessed() {
        return getTotalMetric(context, processedName);
      }

      @Override
      public long getException() {
        Preconditions.checkArgument(exceptionName != null, "exception count not supported");
        return getTotalMetric(context, exceptionName);
      }

      @Override
      public void waitForinput(long count, long timeout, TimeUnit timeoutUnit)
        throws TimeoutException, InterruptedException {
        waitForTotalMetricCount(context, inputName, count, timeout, timeoutUnit);
      }

      @Override
      public void waitForProcessed(long count, long timeout, TimeUnit timeoutUnit)
        throws TimeoutException, InterruptedException {
        waitForTotalMetricCount(context, processedName, count, timeout, timeoutUnit);
      }

      @Override
      public void waitForException(long count, long timeout, TimeUnit timeoutUnit)
        throws TimeoutException, InterruptedException {
        waitForTotalMetricCount(context, exceptionName, count, timeout, timeoutUnit);
      }

      @Override
      public void waitFor(String name, long count,
                          long timeout, TimeUnit timeoutUnit) throws TimeoutException, InterruptedException {
        waitForTotalMetricCount(context, name, count, timeout, timeoutUnit);
      }

      @Override
      public String toString() {
        return String.format("%s; input=%d, processed=%d, exception=%d",
                             Joiner.on(",").withKeyValueSeparator(":").join(context),
                             getInput(), getProcessed(), getException());
      }
    };
  }


  private MetricDataQuery getTotalCounterQuery(Map context, String metricName) {
    return new MetricDataQuery(0, 0, Integer.MAX_VALUE, metricName, AggregationFunction.SUM,
                               context, new ArrayList());
  }

  private long getSingleValueFromTotals(MetricDataQuery query) {
    try {
      Collection result = metricStore.query(query);
      if (result.isEmpty()) {
        return 0;
      }
      // since it is totals query and not groupBy specified, we know there's one time series
      List timeValues = result.iterator().next().getTimeValues();
      if (timeValues.isEmpty()) {
        return 0;
      }

      // since it is totals, we know there's one value only
      return timeValues.get(0).getValue();
    } catch (Exception e) {
      throw Throwables.propagate(e);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy