/*
* 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.app.mapreduce;
import co.cask.cdap.api.metrics.MetricStore;
import co.cask.cdap.api.metrics.MetricType;
import co.cask.cdap.api.metrics.MetricValues;
import co.cask.cdap.app.metrics.MapReduceMetrics;
import co.cask.cdap.common.conf.CConfiguration;
import co.cask.cdap.common.conf.Constants;
import co.cask.cdap.data2.datafabric.dataset.service.DatasetService;
import co.cask.cdap.data2.datafabric.dataset.service.executor.DatasetOpExecutor;
import co.cask.cdap.internal.guice.AppFabricTestModule;
import co.cask.cdap.proto.Id;
import co.cask.cdap.proto.MRJobInfo;
import co.cask.cdap.proto.MRTaskInfo;
import co.cask.cdap.proto.ProgramType;
import co.cask.tephra.TransactionManager;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.apache.hadoop.mapreduce.TaskCounter;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.List;
import java.util.Map;
public class LocalMRJobInfoFetcherTest {
private static Injector injector;
private static MetricStore metricStore;
@BeforeClass
public static void beforeClass() throws Exception {
CConfiguration conf = CConfiguration.create();
conf.set(Constants.AppFabric.OUTPUT_DIR, System.getProperty("java.io.tmpdir"));
conf.set(Constants.AppFabric.TEMP_DIR, System.getProperty("java.io.tmpdir"));
injector = startMetricsService(conf);
metricStore = injector.getInstance(MetricStore.class);
}
public static Injector startMetricsService(CConfiguration conf) {
Injector injector = Guice.createInjector(new AppFabricTestModule(conf));
injector.getInstance(TransactionManager.class).startAndWait();
injector.getInstance(DatasetOpExecutor.class).startAndWait();
injector.getInstance(DatasetService.class).startAndWait();
return injector;
}
@Test
public void testGetMRJobInfo() throws Exception {
Id.Program programId = Id.Program.from("fooNamespace", "testApp", ProgramType.MAPREDUCE, "fooMapReduce");
Id.Run runId = new Id.Run(programId, "run10878");
Map<String, String> runContext = ImmutableMap.of(Constants.Metrics.Tag.NAMESPACE, programId.getNamespaceId(),
Constants.Metrics.Tag.APP, programId.getApplicationId(),
Constants.Metrics.Tag.MAPREDUCE, programId.getId(),
Constants.Metrics.Tag.RUN_ID, runId.getId());
Map<String, String> mapTypeContext =
addToContext(runContext, Constants.Metrics.Tag.MR_TASK_TYPE, MapReduceMetrics.TaskType.Mapper.getId());
Map<String, String> reduceTypeContext =
addToContext(runContext, Constants.Metrics.Tag.MR_TASK_TYPE, MapReduceMetrics.TaskType.Reducer.getId());
String mapTask1Name = "task_m_01";
Map<String, String> mapTask1Context = addToContext(mapTypeContext, Constants.Metrics.Tag.INSTANCE_ID, mapTask1Name);
String mapTask2Name = "task_m_02";
Map<String, String> mapTask2Context = addToContext(mapTypeContext, Constants.Metrics.Tag.INSTANCE_ID, mapTask2Name);
String reduceTaskName = "task_r_01";
Map<String, String> reduceTaskContext =
addToContext(reduceTypeContext, Constants.Metrics.Tag.INSTANCE_ID, reduceTaskName);
// Imitate a MapReduce Job running (gauge mapper and reducer metrics)
long measureTime = System.currentTimeMillis() / 1000;
gauge(mapTypeContext, MapReduceMetrics.METRIC_COMPLETION, measureTime, 76L);
gauge(reduceTypeContext, MapReduceMetrics.METRIC_COMPLETION, measureTime, 52L);
gauge(mapTask1Context, MapReduceMetrics.METRIC_TASK_COMPLETION, measureTime, 100L);
gauge(mapTask1Context, MapReduceMetrics.METRIC_TASK_INPUT_RECORDS, measureTime, 32L);
gauge(mapTask1Context, MapReduceMetrics.METRIC_TASK_OUTPUT_RECORDS, measureTime, 320L);
gauge(mapTask2Context, MapReduceMetrics.METRIC_TASK_COMPLETION, measureTime, 12L);
gauge(mapTask2Context, MapReduceMetrics.METRIC_TASK_INPUT_RECORDS, measureTime, 6L);
gauge(mapTask2Context, MapReduceMetrics.METRIC_TASK_OUTPUT_RECORDS, measureTime, 60L);
// gauge job-level counters for mappers
gauge(mapTypeContext, MapReduceMetrics.METRIC_INPUT_RECORDS, measureTime, 38L);
gauge(mapTypeContext, MapReduceMetrics.METRIC_OUTPUT_RECORDS, measureTime, 380L);
gauge(reduceTaskContext, MapReduceMetrics.METRIC_TASK_COMPLETION, measureTime, 76L);
gauge(reduceTaskContext, MapReduceMetrics.METRIC_TASK_INPUT_RECORDS, measureTime, 320L);
gauge(reduceTaskContext, MapReduceMetrics.METRIC_TASK_OUTPUT_RECORDS, measureTime, 1L);
// gauge job-level counters for reducers
gauge(reduceTypeContext, MapReduceMetrics.METRIC_INPUT_RECORDS, measureTime, 320L);
gauge(reduceTypeContext, MapReduceMetrics.METRIC_OUTPUT_RECORDS, measureTime, 1L);
LocalMRJobInfoFetcher localMRJobInfoFetcher = injector.getInstance(LocalMRJobInfoFetcher.class);
MRJobInfo mrJobInfo = localMRJobInfoFetcher.getMRJobInfo(runId);
// Incomplete because MapReduceMetricsInfo does not provide task-level state and start/end times.
Assert.assertFalse(mrJobInfo.isComplete());
// Check job-level counters
Map<String, Long> jobCounters = mrJobInfo.getCounters();
Assert.assertEquals((Long) 38L, jobCounters.get(TaskCounter.MAP_INPUT_RECORDS.name()));
Assert.assertEquals((Long) 380L, jobCounters.get(TaskCounter.MAP_OUTPUT_RECORDS.name()));
Assert.assertEquals((Long) 320L, jobCounters.get(TaskCounter.REDUCE_INPUT_RECORDS.name()));
Assert.assertEquals((Long) 1L, jobCounters.get(TaskCounter.REDUCE_OUTPUT_RECORDS.name()));
// Ensure all tasks show up
List<MRTaskInfo> mapTasks = mrJobInfo.getMapTasks();
List<MRTaskInfo> reduceTasks = mrJobInfo.getReduceTasks();
Assert.assertEquals(2, mapTasks.size());
Assert.assertEquals(1, reduceTasks.size());
MRTaskInfo mapTask1 = findByTaskId(mapTasks, mapTask1Name);
MRTaskInfo mapTask2 = findByTaskId(mapTasks, mapTask2Name);
MRTaskInfo reduceTask = findByTaskId(reduceTasks, reduceTaskName);
// Check task-level counters
Map<String, Long> mapTask1Counters = mapTask1.getCounters();
Assert.assertEquals((Long) 32L, mapTask1Counters.get(TaskCounter.MAP_INPUT_RECORDS.name()));
Assert.assertEquals((Long) 320L, mapTask1Counters.get(TaskCounter.MAP_OUTPUT_RECORDS.name()));
Map<String, Long> mapTask2Counters = mapTask2.getCounters();
Assert.assertEquals((Long) 6L, mapTask2Counters.get(TaskCounter.MAP_INPUT_RECORDS.name()));
Assert.assertEquals((Long) 60L, mapTask2Counters.get(TaskCounter.MAP_OUTPUT_RECORDS.name()));
Map<String, Long> reduceTaskCounters = reduceTask.getCounters();
Assert.assertEquals((Long) 320L, reduceTaskCounters.get(TaskCounter.REDUCE_INPUT_RECORDS.name()));
Assert.assertEquals((Long) 1L, reduceTaskCounters.get(TaskCounter.REDUCE_OUTPUT_RECORDS.name()));
// Checking progress
float permittedProgressDelta = 0.01F;
Assert.assertEquals(0.76F, mrJobInfo.getMapProgress(), permittedProgressDelta);
Assert.assertEquals(0.52F, mrJobInfo.getReduceProgress(), permittedProgressDelta);
Assert.assertEquals(1.0F, mapTask1.getProgress(), permittedProgressDelta);
Assert.assertEquals(0.12F, mapTask2.getProgress(), permittedProgressDelta);
Assert.assertEquals(0.76F, reduceTask.getProgress(), permittedProgressDelta);
}
private void gauge(Map<String, String> context, String metric, long timestamp, Long value) throws Exception {
metricStore.add(new MetricValues(context, metric, timestamp, value, MetricType.GAUGE));
}
// Returned copied map, with new key-value pair.
private Map<String, String> addToContext(Map<String, String> context, String key, String value) {
return ImmutableMap.<String, String>builder()
.putAll(context)
.put(key, value)
.build();
}
private MRTaskInfo findByTaskId(List<MRTaskInfo> taskInfos, String taskId) {
for (MRTaskInfo taskInfo : taskInfos) {
if (taskInfo.getTaskId().equals(taskId)) {
return taskInfo;
}
}
throw new IllegalArgumentException(
String.format("TaskId: %s not found in list of TaskInfos: %s", taskId, taskInfos));
}
}