/*
* 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.runtime.mapreduce;
import java.util.Map;
import java.util.concurrent.RejectedExecutionException;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.codahale.metrics.Counter;
import com.codahale.metrics.Metric;
import com.codahale.metrics.MetricFilter;
import gobblin.configuration.ConfigurationKeys;
import gobblin.metrics.GobblinMetrics;
import gobblin.runtime.AbstractTaskStateTracker;
import gobblin.runtime.Task;
import gobblin.runtime.util.JobMetrics;
import gobblin.runtime.util.MetricGroup;
import gobblin.source.workunit.WorkUnit;
/**
* A concrete extension to {@link gobblin.runtime.AbstractTaskStateTracker} for Hadoop MapReduce based runtime.
*
* @author Yinan Li
*/
public class MRTaskStateTracker extends AbstractTaskStateTracker {
private static final Logger LOG = LoggerFactory.getLogger(MRTaskStateTracker.class);
// Mapper context used to signal progress and update counters
private final Mapper<LongWritable, Text, NullWritable, NullWritable>.Context context;
public MRTaskStateTracker(Mapper<LongWritable, Text, NullWritable, NullWritable>.Context context) {
super(context.getConfiguration(), LOG);
this.context = context;
}
@Override
public void registerNewTask(Task task) {
try {
scheduleTaskMetricsUpdater(new MRTaskMetricsUpdater(task, this.context), task);
} catch (RejectedExecutionException ree) {
LOG.error(String.format("Scheduling of task state reporter for task %s was rejected", task.getTaskId()));
}
}
@Override
public void onTaskRunCompletion(Task task) {
task.markTaskCompletion();
}
@Override
public void onTaskCommitCompletion(Task task) {
WorkUnit workUnit = task.getTaskState().getWorkunit();
if (GobblinMetrics.isEnabled(workUnit)) {
task.updateRecordMetrics();
task.updateByteMetrics();
if (workUnit.getPropAsBoolean(ConfigurationKeys.MR_REPORT_METRICS_AS_COUNTERS_KEY,
ConfigurationKeys.DEFAULT_MR_REPORT_METRICS_AS_COUNTERS)) {
updateCounters(task);
}
}
LOG.info(String
.format("Task %s completed running in %dms with state %s", task.getTaskId(), task.getTaskState().getTaskDuration(),
task.getTaskState().getWorkingState()));
}
/**
* An extension to {@link AbstractTaskStateTracker.TaskMetricsUpdater} for updating task metrics
* in the Hadoop MapReduce setting.
*/
private class MRTaskMetricsUpdater extends AbstractTaskStateTracker.TaskMetricsUpdater {
private final Mapper<LongWritable, Text, NullWritable, NullWritable>.Context context;
MRTaskMetricsUpdater(Task task, Mapper<LongWritable, Text, NullWritable, NullWritable>.Context context) {
super(task);
this.context = context;
}
@Override
protected void updateTaskMetrics() {
super.updateTaskMetrics();
WorkUnit workUnit = this.task.getTaskState().getWorkunit();
if (GobblinMetrics.isEnabled(workUnit)) {
if (workUnit.getPropAsBoolean(ConfigurationKeys.MR_REPORT_METRICS_AS_COUNTERS_KEY,
ConfigurationKeys.DEFAULT_MR_REPORT_METRICS_AS_COUNTERS)) {
updateCounters(this.task);
}
}
// Tell the TaskTracker it's making progress
this.context.progress();
}
}
private void updateCounters(Task task) {
updateCounters(task, MetricGroupFilter.JOB);
updateCounters(task, MetricGroupFilter.TASK);
}
private void updateCounters(Task task, MetricGroupFilter filter) {
Map<String, Counter> counters = JobMetrics.get(null, task.getJobId()).getMetricContext().getCounters(filter);
if (counters != null) {
for (Map.Entry<String, Counter> entry : counters.entrySet()) {
this.context.getCounter(filter.getGroupName(), entry.getKey()).setValue(entry.getValue().getCount());
}
}
}
private enum MetricGroupFilter implements MetricFilter {
JOB() {
@Override
public String getGroupName() {
return MetricGroup.JOB.toString();
}
},
TASK() {
@Override
public String getGroupName() {
return MetricGroup.TASK.toString();
}
};
@Override
public boolean matches(String name, Metric metric) {
return name.startsWith(this.toString()) ? true : false;
}
public abstract String getGroupName();
}
}