co.cask.cdap.internal.app.services.http.handlers.WorkflowStatsSLAHttpHandlerTest.java Source code

Java tutorial

Introduction

Here is the source code for co.cask.cdap.internal.app.services.http.handlers.WorkflowStatsSLAHttpHandlerTest.java

Source

/*
 * Copyright  2015-2016 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.services.http.handlers;

import co.cask.cdap.WorkflowApp;
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.app.store.Store;
import co.cask.cdap.common.app.RunIds;
import co.cask.cdap.common.conf.Constants;
import co.cask.cdap.gateway.handlers.WorkflowStatsSLAHttpHandler;
import co.cask.cdap.internal.app.runtime.ProgramOptionConstants;
import co.cask.cdap.internal.app.services.http.AppFabricTestBase;
import co.cask.cdap.internal.app.store.DefaultStore;
import co.cask.cdap.proto.Id;
import co.cask.cdap.proto.PercentileInformation;
import co.cask.cdap.proto.ProgramRunStatus;
import co.cask.cdap.proto.ProgramType;
import co.cask.cdap.proto.WorkflowStatistics;
import co.cask.cdap.proto.WorkflowStatsComparison;
import com.google.common.collect.ImmutableMap;
import com.google.gson.reflect.TypeToken;
import org.apache.hadoop.mapreduce.TaskCounter;
import org.apache.http.HttpResponse;
import org.apache.twill.api.RunId;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Tests for {@link WorkflowStatsSLAHttpHandler}
 */
public class WorkflowStatsSLAHttpHandlerTest extends AppFabricTestBase {

    private static MetricStore metricStore;
    private static Store store;

    @BeforeClass
    public static void beforeClass() throws Throwable {
        AppFabricTestBase.beforeClass();
        store = getInjector().getInstance(DefaultStore.class);
        metricStore = getInjector().getInstance(MetricStore.class);
    }

    @Test
    public void testStatistics() throws Exception {

        deploy(WorkflowApp.class);
        String workflowName = "FunWorkflow";
        String mapreduceName = "ClassicWordCount";
        String sparkName = "SparkWorkflowTest";

        Id.Program workflowProgram = Id.Workflow.from(Id.Namespace.DEFAULT, "WorkflowApp", ProgramType.WORKFLOW,
                workflowName);
        Id.Program mapreduceProgram = Id.Program.from(Id.Namespace.DEFAULT, "WorkflowApp", ProgramType.MAPREDUCE,
                mapreduceName);
        Id.Program sparkProgram = Id.Program.from(Id.Namespace.DEFAULT, "WorkflowApp", ProgramType.SPARK,
                sparkName);

        long startTime = System.currentTimeMillis();
        long currentTimeMillis = startTime;
        String outlierRunId = null;
        for (int i = 0; i < 10; i++) {
            // workflow runs every 5 minutes
            currentTimeMillis = startTime + (i * TimeUnit.MINUTES.toMillis(5));
            RunId workflowRunId = RunIds.generate(currentTimeMillis);
            store.setStart(workflowProgram, workflowRunId.getId(), RunIds.getTime(workflowRunId, TimeUnit.SECONDS));

            // MR job starts 2 seconds after workflow started
            RunId mapreduceRunid = RunIds.generate(currentTimeMillis + TimeUnit.SECONDS.toMillis(2));
            Map<String, String> systemArgs = ImmutableMap.of(ProgramOptionConstants.WORKFLOW_NODE_ID, mapreduceName,
                    ProgramOptionConstants.WORKFLOW_NAME, workflowName, ProgramOptionConstants.WORKFLOW_RUN_ID,
                    workflowRunId.getId());

            store.setStart(mapreduceProgram, mapreduceRunid.getId(),
                    RunIds.getTime(mapreduceRunid, TimeUnit.SECONDS), null, ImmutableMap.<String, String>of(),
                    systemArgs);

            store.setStop(mapreduceProgram, mapreduceRunid.getId(),
                    // map-reduce job ran for 17 seconds
                    TimeUnit.MILLISECONDS.toSeconds(currentTimeMillis) + 19, ProgramRunStatus.COMPLETED);

            // This makes sure that not all runs have Spark programs in them
            if (i < 5) {
                // spark starts 20 seconds after workflow starts
                RunId sparkRunid = RunIds.generate(currentTimeMillis + TimeUnit.SECONDS.toMillis(20));
                systemArgs = ImmutableMap.of(ProgramOptionConstants.WORKFLOW_NODE_ID, sparkProgram.getId(),
                        ProgramOptionConstants.WORKFLOW_NAME, workflowName, ProgramOptionConstants.WORKFLOW_RUN_ID,
                        workflowRunId.getId());
                store.setStart(sparkProgram, sparkRunid.getId(), RunIds.getTime(sparkRunid, TimeUnit.SECONDS), null,
                        ImmutableMap.<String, String>of(), systemArgs);

                // spark job runs for 38 seconds
                long stopTime = TimeUnit.MILLISECONDS.toSeconds(currentTimeMillis) + 58;
                if (i == 4) {
                    // spark job ran for 100 seconds. 62 seconds greater than avg.
                    stopTime = TimeUnit.MILLISECONDS.toSeconds(currentTimeMillis) + 120;
                }
                store.setStop(sparkProgram, sparkRunid.getId(), stopTime, ProgramRunStatus.COMPLETED);
            }

            // workflow ran for 1 minute
            long workflowStopTime = TimeUnit.MILLISECONDS.toSeconds(currentTimeMillis) + 60;
            if (i == 4) {
                // spark job ran longer for this run
                workflowStopTime = TimeUnit.MILLISECONDS.toSeconds(currentTimeMillis) + 122;
                outlierRunId = workflowRunId.getId();
            }

            store.setStop(workflowProgram, workflowRunId.getId(), workflowStopTime, ProgramRunStatus.COMPLETED);
        }

        String request = String.format(
                "%s/namespaces/%s/apps/%s/workflows/%s/statistics?start=%s&end=%s" + "&percentile=%s",
                Constants.Gateway.API_VERSION_3, Id.Namespace.DEFAULT.getId(), WorkflowApp.class.getSimpleName(),
                workflowProgram.getId(), TimeUnit.MILLISECONDS.toSeconds(startTime),
                TimeUnit.MILLISECONDS.toSeconds(currentTimeMillis) + TimeUnit.MINUTES.toSeconds(2), "99");

        HttpResponse response = doGet(request);
        WorkflowStatistics workflowStatistics = readResponse(response, new TypeToken<WorkflowStatistics>() {
        }.getType());
        PercentileInformation percentileInformation = workflowStatistics.getPercentileInformationList().get(0);
        Assert.assertEquals(1, percentileInformation.getRunIdsOverPercentile().size());
        Assert.assertEquals(outlierRunId, percentileInformation.getRunIdsOverPercentile().get(0));
        Assert.assertEquals("5", workflowStatistics.getNodes().get(sparkName).get("runs"));

        request = String.format(
                "%s/namespaces/%s/apps/%s/workflows/%s/statistics?start=%s&end=%s" + "&percentile=%s&percentile=%s",
                Constants.Gateway.API_VERSION_3, Id.Namespace.DEFAULT.getId(), WorkflowApp.class.getSimpleName(),
                workflowProgram.getId(), "now", "0", "90", "95");

        response = doGet(request);
        Assert.assertEquals(HttpResponseStatus.BAD_REQUEST.getCode(), response.getStatusLine().getStatusCode());

        request = String.format(
                "%s/namespaces/%s/apps/%s/workflows/%s/statistics?start=%s&end=%s" + "&percentile=%s&percentile=%s",
                Constants.Gateway.API_VERSION_3, Id.Namespace.DEFAULT.getId(), WorkflowApp.class.getSimpleName(),
                workflowProgram.getId(), "now", "0", "90.0", "950");

        response = doGet(request);
        Assert.assertEquals(HttpResponseStatus.BAD_REQUEST.getCode(), response.getStatusLine().getStatusCode());
    }

    @Test
    public void testDetails() throws Exception {
        deploy(WorkflowApp.class);
        String workflowName = "FunWorkflow";
        String mapreduceName = "ClassicWordCount";
        String sparkName = "SparkWorkflowTest";

        Id.Program workflowProgram = Id.Workflow.from(Id.Namespace.DEFAULT, "WorkflowApp", ProgramType.WORKFLOW,
                workflowName);
        Id.Program mapreduceProgram = Id.Program.from(Id.Namespace.DEFAULT, "WorkflowApp", ProgramType.MAPREDUCE,
                mapreduceName);
        Id.Program sparkProgram = Id.Program.from(Id.Namespace.DEFAULT, "WorkflowApp", ProgramType.SPARK,
                sparkName);

        List<RunId> runIdList = setupRuns(workflowProgram, mapreduceProgram, sparkProgram, store, 13);

        String request = String.format(
                "%s/namespaces/%s/apps/%s/workflows/%s/runs/%s/statistics?limit=%s&interval=%s",
                Constants.Gateway.API_VERSION_3, Id.Namespace.DEFAULT.getId(), WorkflowApp.class.getSimpleName(),
                workflowProgram.getId(), runIdList.get(6).getId(), "3", "10m");

        HttpResponse response = doGet(request);
        WorkflowStatsComparison workflowStatistics = readResponse(response,
                new TypeToken<WorkflowStatsComparison>() {
                }.getType());

        Assert.assertEquals(7,
                workflowStatistics.getProgramNodesList().iterator().next().getWorkflowProgramDetailsList().size());

        request = String.format("%s/namespaces/%s/apps/%s/workflows/%s/runs/%s/statistics?limit=0",
                Constants.Gateway.API_VERSION_3, Id.Namespace.DEFAULT.getId(), WorkflowApp.class.getSimpleName(),
                workflowProgram.getId(), runIdList.get(6).getId());

        response = doGet(request);
        Assert.assertEquals(HttpResponseStatus.BAD_REQUEST.getCode(), response.getStatusLine().getStatusCode());

        request = String.format("%s/namespaces/%s/apps/%s/workflows/%s/runs/%s/statistics?limit=10&interval=10",
                Constants.Gateway.API_VERSION_3, Id.Namespace.DEFAULT.getId(), WorkflowApp.class.getSimpleName(),
                workflowProgram.getId(), runIdList.get(6).getId());

        response = doGet(request);
        Assert.assertEquals(HttpResponseStatus.BAD_REQUEST.getCode(), response.getStatusLine().getStatusCode());

        request = String.format("%s/namespaces/%s/apps/%s/workflows/%s/runs/%s/statistics?limit=10&interval=10P",
                Constants.Gateway.API_VERSION_3, Id.Namespace.DEFAULT.getId(), WorkflowApp.class.getSimpleName(),
                workflowProgram.getId(), runIdList.get(6).getId());

        response = doGet(request);
        Assert.assertEquals(HttpResponseStatus.BAD_REQUEST.getCode(), response.getStatusLine().getStatusCode());

        request = String.format("%s/namespaces/%s/apps/%s/workflows/%s/runs/%s/statistics?limit=20&interval=0d",
                Constants.Gateway.API_VERSION_3, Id.Namespace.DEFAULT.getId(), WorkflowApp.class.getSimpleName(),
                workflowProgram.getId(), runIdList.get(6).getId());

        response = doGet(request);
        Assert.assertEquals(HttpResponseStatus.BAD_REQUEST.getCode(), response.getStatusLine().getStatusCode());
    }

    @Test
    public void testCompare() throws Exception {
        deploy(WorkflowApp.class);
        String workflowName = "FunWorkflow";
        String mapreduceName = "ClassicWordCount";
        String sparkName = "SparkWorkflowTest";

        Id.Program workflowProgram = Id.Workflow.from(Id.Namespace.DEFAULT, "WorkflowApp", ProgramType.WORKFLOW,
                workflowName);
        Id.Program mapreduceProgram = Id.Program.from(Id.Namespace.DEFAULT, "WorkflowApp", ProgramType.MAPREDUCE,
                mapreduceName);
        Id.Program sparkProgram = Id.Program.from(Id.Namespace.DEFAULT, "WorkflowApp", ProgramType.SPARK,
                sparkName);

        List<RunId> workflowRunIdList = setupRuns(workflowProgram, mapreduceProgram, sparkProgram, store, 2);
        RunId workflowRun1 = workflowRunIdList.get(0);
        RunId workflowRun2 = workflowRunIdList.get(1);

        String request = String.format("%s/namespaces/%s/apps/%s/workflows/%s/runs/%s/compare?other-run-id=%s",
                Constants.Gateway.API_VERSION_3, Id.Namespace.DEFAULT.getId(), WorkflowApp.class.getSimpleName(),
                workflowProgram.getId(), workflowRun1.toString(), workflowRun2.toString());

        HttpResponse response = doGet(request);
        Collection<WorkflowStatsComparison.ProgramNodes> workflowStatistics = readResponse(response,
                new TypeToken<Collection<WorkflowStatsComparison.ProgramNodes>>() {
                }.getType());

        Assert.assertNotNull(workflowStatistics.iterator().next());
        Assert.assertEquals(2, workflowStatistics.size());

        for (WorkflowStatsComparison.ProgramNodes node : workflowStatistics) {
            if (node.getProgramType() == ProgramType.MAPREDUCE) {
                Assert.assertEquals(38L, (long) node.getWorkflowProgramDetailsList().get(0).getMetrics()
                        .get(TaskCounter.MAP_INPUT_RECORDS.name()));
            }
        }
    }

    /*
     * This helper is used only for the details and compare endpoints and not the statistics endpoint because
     * the statistics endpoint needs to handle number of spark runs differently and also have tests for a
     * specific run's spark job.
     */
    private List<RunId> setupRuns(Id.Program workflowProgram, Id.Program mapreduceProgram, Id.Program sparkProgram,
            Store store, int count) throws Exception {
        List<RunId> runIdList = new ArrayList<>();
        long startTime = System.currentTimeMillis();
        long currentTimeMillis;
        for (int i = 0; i < count; i++) {
            // work-flow runs every 5 minutes
            currentTimeMillis = startTime + (i * TimeUnit.MINUTES.toMillis(5));
            RunId workflowRunId = RunIds.generate(currentTimeMillis);
            runIdList.add(workflowRunId);
            store.setStart(workflowProgram, workflowRunId.getId(), RunIds.getTime(workflowRunId, TimeUnit.SECONDS));

            // MR job starts 2 seconds after workflow started
            RunId mapreduceRunid = RunIds.generate(currentTimeMillis + TimeUnit.SECONDS.toMillis(2));
            Map<String, String> systemArgs = ImmutableMap.of(ProgramOptionConstants.WORKFLOW_NODE_ID,
                    mapreduceProgram.getId(), ProgramOptionConstants.WORKFLOW_NAME, workflowProgram.getId(),
                    ProgramOptionConstants.WORKFLOW_RUN_ID, workflowRunId.getId());

            store.setStart(mapreduceProgram, mapreduceRunid.getId(),
                    RunIds.getTime(mapreduceRunid, TimeUnit.SECONDS), null, ImmutableMap.<String, String>of(),
                    systemArgs);
            store.setStop(mapreduceProgram, mapreduceRunid.getId(),
                    // map-reduce job ran for 17 seconds
                    TimeUnit.MILLISECONDS.toSeconds(currentTimeMillis) + 19, ProgramRunStatus.COMPLETED);

            Map<String, String> mapTypeContext = ImmutableMap.of(Constants.Metrics.Tag.NAMESPACE,
                    mapreduceProgram.getNamespaceId(), Constants.Metrics.Tag.APP,
                    mapreduceProgram.getApplicationId(), Constants.Metrics.Tag.MAPREDUCE, mapreduceProgram.getId(),
                    Constants.Metrics.Tag.RUN_ID, mapreduceRunid.toString(), Constants.Metrics.Tag.MR_TASK_TYPE,
                    MapReduceMetrics.TaskType.Mapper.getId());

            metricStore.add(new MetricValues(mapTypeContext, MapReduceMetrics.METRIC_INPUT_RECORDS, 10, 38L,
                    MetricType.GAUGE));

            // spark starts 20 seconds after workflow starts
            systemArgs = ImmutableMap.of(ProgramOptionConstants.WORKFLOW_NODE_ID, sparkProgram.getId(),
                    ProgramOptionConstants.WORKFLOW_NAME, workflowProgram.getId(),
                    ProgramOptionConstants.WORKFLOW_RUN_ID, workflowRunId.getId());
            RunId sparkRunid = RunIds.generate(currentTimeMillis + TimeUnit.SECONDS.toMillis(20));
            store.setStart(sparkProgram, sparkRunid.getId(), RunIds.getTime(sparkRunid, TimeUnit.SECONDS), null,
                    ImmutableMap.<String, String>of(), systemArgs);

            // spark job runs for 38 seconds
            long stopTime = TimeUnit.MILLISECONDS.toSeconds(currentTimeMillis) + 58;
            store.setStop(sparkProgram, sparkRunid.getId(), stopTime, ProgramRunStatus.COMPLETED);

            // workflow ran for 1 minute
            long workflowStopTime = TimeUnit.MILLISECONDS.toSeconds(currentTimeMillis) + 60;

            store.setStop(workflowProgram, workflowRunId.getId(), workflowStopTime, ProgramRunStatus.COMPLETED);
        }
        return runIdList;
    }
}