co.cask.cdap.test.MetricsManager.java Source code

Java tutorial

Introduction

Here is the source code for co.cask.cdap.test.MetricsManager.java

Source

/*
 * 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<MetricTimeSeries>
     * @param query
     * @return Collection<MetricTimeSeries>
     * @throws Exception
     */
    public Collection<MetricTimeSeries> 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<String, String> 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<String, String> 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<String, String> 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<String, String> context, String metricName) {
        return new MetricDataQuery(0, 0, Integer.MAX_VALUE, metricName, AggregationFunction.SUM, context,
                new ArrayList<String>());
    }

    private long getSingleValueFromTotals(MetricDataQuery query) {
        try {
            Collection<MetricTimeSeries> 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<TimeValue> 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);
        }
    }
}