/* * Copyright © 2014 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.internal.app.runtime.batch; import co.cask.cdap.api.ProgramLifecycle; import co.cask.cdap.api.annotation.UseDataSet; import co.cask.cdap.api.common.Bytes; import co.cask.cdap.api.dataset.lib.TimeseriesTable; import co.cask.cdap.api.dataset.table.Increment; import co.cask.cdap.api.dataset.table.Table; import co.cask.cdap.api.mapreduce.MapReduceContext; import co.cask.cdap.api.metrics.Metrics; import org.apache.hadoop.io.BytesWritable; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.Mapper; import org.apache.hadoop.mapreduce.Reducer; import org.junit.Assert; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; /** * */ public class AggregateMetricsByTag { public static final Logger LOG = LoggerFactory.getLogger(AggregateMetricsByTag.class); public static final byte[] BY_TAGS = Bytes.toBytes("byTag"); /** * */ public static class Map extends Mapper<byte[], TimeseriesTable.Entry, BytesWritable, LongWritable> implements ProgramLifecycle<MapReduceContext> { private Metrics metrics; @UseDataSet("counters") private Table counters; private Table countersFromContext; public void map(byte[] key, TimeseriesTable.Entry value, Context context) throws IOException, InterruptedException { metrics.count("in.map", 1); for (byte[] tag : value.getTags()) { long val = Bytes.toLong(value.getValue()); if (55L == val) { throw new RuntimeException("Intentional exception: someone on purpose added bad data as input"); } context.write(new BytesWritable(tag), new LongWritable(val)); } counters.increment(new Increment("mapper", "records", 1L)); countersFromContext.increment(new Increment("mapper", "records", 1L)); metrics.count("in.map", 2); } @Override protected void setup(Context context) throws IOException, InterruptedException { metrics.gauge("in.map.setup", 1); LOG.info("in mapper: setup()"); long mappersCount = counters.incrementAndGet(new Increment("mapper", "count", 1L)).getLong("count", 0); Assert.assertEquals(mappersCount, countersFromContext.incrementAndGet(new Increment("mapper", "count", 1L)) .getLong("count", 0)); LOG.info("mappers started so far: " + mappersCount); metrics.gauge("in.map.setup", 1); } @Override public void initialize(MapReduceContext context) throws Exception { countersFromContext = context.getDataset("countersFromContext"); } @Override public void destroy() { // do nothing } } /** * */ public static class Reduce extends Reducer<BytesWritable, LongWritable, byte[], TimeseriesTable.Entry> implements ProgramLifecycle<MapReduceContext> { private Metrics metrics; @UseDataSet("counters") private Table counters; private Table countersFromContext; public void reduce(BytesWritable key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException { metrics.count("in.reduce", 1); long sum = 0; for (LongWritable val : values) { sum += val.get(); counters.increment(new Increment("reducer", "records", 1L)); countersFromContext.increment(new Increment("reducer", "records", 1L)); } byte[] tag = key.copyBytes(); context.write(tag, new TimeseriesTable.Entry(BY_TAGS, Bytes.toBytes(sum), System.currentTimeMillis(), tag)); metrics.count("in.reduce", 1); } @Override protected void setup(Reducer.Context context) throws IOException, InterruptedException { metrics.gauge("in.reduce.setup", 1); LOG.info("in reducer: setup()"); long reducersCount = counters.incrementAndGet(new Increment("reducer", "count", 1L)).getLong("count", 0); Assert.assertEquals(reducersCount, countersFromContext.incrementAndGet(new Increment("reducer", "count", 1L)) .getLong("count", 0)); LOG.info("reducers started so far: " + reducersCount); metrics.gauge("in.reduce.setup", 1); } @Override public void initialize(MapReduceContext context) throws Exception { countersFromContext = context.getDataset("countersFromContext"); } @Override public void destroy() { // do nothing } } static void configureJob(Job job) throws IOException { job.setMapperClass(Map.class); job.setMapOutputKeyClass(BytesWritable.class); job.setMapOutputValueClass(LongWritable.class); job.setReducerClass(Reduce.class); } }