/*
* Copyright © 2014-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.metrics.collect;
import co.cask.cdap.api.metrics.MetricValue;
import co.cask.cdap.api.metrics.MetricValues;
import co.cask.cdap.api.metrics.MetricsContext;
import co.cask.cdap.common.conf.Constants;
import co.cask.cdap.test.SlowTests;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterators;
import org.junit.Assert;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* Testing the basic properties of the {@link AggregatedMetricsCollectionService}.
*/
public class AggregatedMetricsCollectionServiceTest {
private static final HashMap<String, String> EMPTY_TAGS = new HashMap<>();
private static final String NAMESPACE = "testnamespace";
private static final String APP = "testapp";
private static final String FLOW = "testprogram";
private static final String RUNID = "testrun";
private static final String FLOWLET = "testflowlet";
private static final String INSTANCE = "testInstance";
private static final String METRIC = "metric";
private long getMetricValue(Collection<MetricValue> metrics, String metricName) {
Iterator<MetricValue> metricsItor = metrics.iterator();
while (metricsItor.hasNext()) {
MetricValue metricValue = metricsItor.next();
if (metricValue.getName().equals(metricName)) {
return metricValue.getValue();
}
}
return 0;
}
@Category(SlowTests.class)
@Test
public void testPublish() throws InterruptedException {
final BlockingQueue<MetricValues> published = new LinkedBlockingQueue<>();
AggregatedMetricsCollectionService service = new AggregatedMetricsCollectionService() {
@Override
protected void publish(Iterator<MetricValues> metrics) {
Iterators.addAll(published, metrics);
}
@Override
protected Scheduler scheduler() {
return Scheduler.newFixedRateSchedule(5, 1, TimeUnit.SECONDS);
}
};
service.startAndWait();
// non-empty tags.
final Map baseTags = ImmutableMap.of(Constants.Metrics.Tag.NAMESPACE, NAMESPACE,
Constants.Metrics.Tag.APP, APP,
Constants.Metrics.Tag.FLOW, FLOW,
Constants.Metrics.Tag.RUN_ID, RUNID);
try {
// The first section tests with empty tags.
// Publish couple metrics with empty tags, they should be aggregated.
service.getContext(EMPTY_TAGS).increment(METRIC, Integer.MAX_VALUE);
service.getContext(EMPTY_TAGS).increment(METRIC, 2);
service.getContext(EMPTY_TAGS).increment(METRIC, 3);
service.getContext(EMPTY_TAGS).increment(METRIC, 4);
MetricValues record = published.poll(10, TimeUnit.SECONDS);
Assert.assertNotNull(record);
Assert.assertEquals(((long) Integer.MAX_VALUE) + 9L, getMetricValue(record.getMetrics(), METRIC));
// No publishing for 0 value metrics
Assert.assertNull(published.poll(3, TimeUnit.SECONDS));
// Publish a metric and wait for it so that we know there is around 1 second to publish more metrics to test.
service.getContext(EMPTY_TAGS).increment(METRIC, 1);
Assert.assertNotNull(published.poll(3, TimeUnit.SECONDS));
//update the metrics multiple times with gauge.
service.getContext(EMPTY_TAGS).gauge(METRIC, 1);
service.getContext(EMPTY_TAGS).gauge(METRIC, 2);
service.getContext(EMPTY_TAGS).gauge(METRIC, 3);
// gauge just updates the value, so polling should return the most recent value written
record = published.poll(3, TimeUnit.SECONDS);
Assert.assertNotNull(record);
Assert.assertEquals(3, getMetricValue(record.getMetrics(), METRIC));
// define collectors for non-empty tags
MetricsContext baseCollector = service.getContext(baseTags);
MetricsContext flowletInstanceCollector = baseCollector.childContext(Constants.Metrics.Tag.FLOWLET, FLOWLET)
.childContext(Constants.Metrics.Tag.INSTANCE_ID, INSTANCE);
// increment metrics for various collectors
baseCollector.increment(METRIC, Integer.MAX_VALUE);
flowletInstanceCollector.increment(METRIC, 5);
baseCollector.increment(METRIC, 10);
baseCollector.increment(METRIC, 3);
flowletInstanceCollector.increment(METRIC, 2);
flowletInstanceCollector.increment(METRIC, 4);
flowletInstanceCollector.increment(METRIC, 3);
flowletInstanceCollector.increment(METRIC, 1);
// there are two collectors, verify their metrics values
verifyCounterMetricsValue(published.poll(10, TimeUnit.SECONDS));
verifyCounterMetricsValue(published.poll(10, TimeUnit.SECONDS));
// No publishing for 0 value metrics
Assert.assertNull(published.poll(3, TimeUnit.SECONDS));
// gauge metrics for various collectors
baseCollector.gauge(METRIC, Integer.MAX_VALUE);
baseCollector.gauge(METRIC, 3);
flowletInstanceCollector.gauge(METRIC, 6);
flowletInstanceCollector.gauge(METRIC, 2);
baseCollector.gauge(METRIC, 1);
flowletInstanceCollector.gauge(METRIC, Integer.MAX_VALUE);
// gauge just updates the value, so polling should return the most recent value written
verifyGaugeMetricsValue(published.poll(10, TimeUnit.SECONDS));
verifyGaugeMetricsValue(published.poll(10, TimeUnit.SECONDS));
flowletInstanceCollector.gauge(METRIC, 0);
verifyMetricsValue(published.poll(10, TimeUnit.SECONDS), 0L);
} finally {
service.stopAndWait();
}
}
private void verifyCounterMetricsValue(MetricValues metricValues) {
Assert.assertNotNull(metricValues);
Map<String, String> tags = metricValues.getTags();
if (tags.size() == 4) {
// base collector
Assert.assertEquals(((long) Integer.MAX_VALUE) + 13L, getMetricValue(metricValues.getMetrics(), METRIC));
} else if (tags.size() == 6) {
// flowlet collector
Assert.assertEquals(15L, getMetricValue(metricValues.getMetrics(), METRIC));
} else {
Assert.fail("Unexpected number of tags while verifying counter metrics value - " + tags.size());
}
}
private void verifyGaugeMetricsValue(MetricValues metricValues) {
Assert.assertNotNull(metricValues);
Map<String, String> tags = metricValues.getTags();
if (tags.size() == 4) {
// base collector
Assert.assertEquals(1L, getMetricValue(metricValues.getMetrics(), METRIC));
} else if (tags.size() == 6) {
// flowlet collector
Assert.assertEquals((long) Integer.MAX_VALUE, getMetricValue(metricValues.getMetrics(), METRIC));
} else {
Assert.fail("Unexpected number of tags while verifying gauge metrics value - " + tags.size());
}
}
private void verifyMetricsValue(MetricValues metricValues, long expected) {
Assert.assertNotNull(metricValues);
Assert.assertEquals(expected, getMetricValue(metricValues.getMetrics(), METRIC));
}
}