/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 gobblin.metrics.reporter;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import org.testng.Assert;
import org.testng.annotations.Test;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.typesafe.config.Config;
import gobblin.metrics.MetricContext;
import gobblin.metrics.context.ReportableContext;
import gobblin.metrics.context.filter.ContextFilterFactory;
import gobblin.metrics.test.ContextStoreReporter;
import gobblin.util.ConfigUtils;
/**
* Test for {@link gobblin.metrics.reporter.ScheduledReporter}
*/
public class ScheduledReporterTest {
@Test
public void testPeriodParser() {
Assert.assertEquals(ScheduledReporter.parsePeriodToSeconds("1s"), 1);
Assert.assertEquals(ScheduledReporter.parsePeriodToSeconds("2m"), 120);
Assert.assertEquals(ScheduledReporter.parsePeriodToSeconds("3h"), 3 * 3600);
Assert.assertEquals(ScheduledReporter.parsePeriodToSeconds("1m2s"), 62);
Assert.assertEquals(ScheduledReporter.parsePeriodToSeconds("1h1s"), 3601);
Assert.assertEquals(ScheduledReporter.parsePeriodToSeconds("1h2m3s"), 3600 + 120 + 3);
try {
ScheduledReporter.parsePeriodToSeconds("1000000h");
Assert.fail();
} catch (RuntimeException re) {
// fail unless exception is thrown
}
}
@Test
public void testScheduledReporter() throws Exception {
long reportingIntervalMillis = 1000;
String context1Name = ScheduledReporterTest.class.getSimpleName() + "_1";
String context2Name = ScheduledReporterTest.class.getSimpleName() + "_2";
String context3Name = "SomeOtherName";
// Create a context name (to check that initialized reporter gets existing contexts correctly)
MetricContext context1 = MetricContext.builder(context1Name).build();
// Set up config for reporter
Properties props = new Properties();
ScheduledReporter.setReportingInterval(props, reportingIntervalMillis, TimeUnit.MILLISECONDS);
Config config = ConfigUtils.propertiesToConfig(props);
config = PrefixContextFilter.setPrefixString(config, ScheduledReporterTest.class.getSimpleName());
config = ContextFilterFactory.setContextFilterClass(config, PrefixContextFilter.class);
// Create reporter
ContextStoreReporter reporter = new ContextStoreReporter("testContext", config);
// Check that reporter correctly found created context
Set<String> contextNames = getContextNames(reporter);
Assert.assertEquals(contextNames.size(), 1);
Assert.assertTrue(contextNames.contains(context1Name));
// Create two more contexts
MetricContext context2 = context1.childBuilder(context2Name).build();
context1.childBuilder(context3Name).build();
// Check that reporter correctly found new reporter, but skipped the one that does not satisfy filter
contextNames = getContextNames(reporter);
Assert.assertEquals(contextNames.size(), 2);
Assert.assertTrue(contextNames.contains(context1Name));
Assert.assertTrue(contextNames.contains(context2Name));
// Check that nothing has been reported
Assert.assertEquals(reporter.getReportedContexts().size(), 0);
// Start reporter
reporter.start();
// Wait for up to 10 reporting intervals for 3 reports to run
long maxWaitMillis = 10 * reportingIntervalMillis;
long totalWait = 0;
while(reporter.getReportedContexts().size() < 6 && maxWaitMillis > 0) {
long wait = 100;
Thread.sleep(wait);
maxWaitMillis -= wait;
totalWait += wait;
}
// stop reporter
reporter.stop();
// Check wait makes sense given reporting interval (e.g. if wait = 100 millis, then 2 reports in 100 millis,
// something is wrong with schedule).
Assert.assertTrue(totalWait > reportingIntervalMillis);
Assert.assertTrue(reporter.getReportedContexts().size() >= 6);
// Check that it didn't report excessively
Assert.assertTrue(reporter.getReportedContexts().size() <= 10);
// Check that first report indeed reported the correct contexts
Set<String> firstReport = Sets.newHashSet(reporter.getReportedContexts().get(0).getName(),
reporter.getReportedContexts().get(1).getName());
Assert.assertEquals(firstReport, Sets.newHashSet(context1Name, context2Name));
// Check that second report indeed reported the correct contexts
Set<String> secondReport = Sets.newHashSet(reporter.getReportedContexts().get(2).getName(),
reporter.getReportedContexts().get(3).getName());
Assert.assertEquals(secondReport, Sets.newHashSet(context1Name, context2Name));
int totalReports = reporter.getReportedContexts().size();
// Wait for reporting interval to make sure reporting has actually stopped
Thread.sleep(2 * reportingIntervalMillis);
Assert.assertEquals(reporter.getReportedContexts().size(), totalReports);
reporter.getReportedContexts().clear();
// Dereference context 2 to ensure that it gets reported
context2 = null;
// Wait for context to be GCed
maxWaitMillis = 2000;
System.gc();
while(reporter.getReportedContexts().size() < 1 && maxWaitMillis > 0) {
System.gc();
long wait = 100;
Thread.sleep(wait);
maxWaitMillis -= wait;
}
// Check that GCed context was reported
Assert.assertEquals(reporter.getReportedContexts().size(), 1);
Assert.assertEquals(reporter.getReportedContexts().get(0).getName(), context2Name);
// Test close method
reporter.close();
}
private Set<String> getContextNames(ContextStoreReporter reporter) {
return Sets.newHashSet(Iterables.transform(reporter.getContextsToReport(),
new Function<ReportableContext, String>() {
@Nullable @Override public String apply(ReportableContext input) {
return input.getName();
}
}));
}
}