All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gobblin.service.FlowStatusResource Maven / Gradle / Ivy

The newest version!
/*
 * 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.service;

import com.linkedin.restli.server.PagingContext;
import com.linkedin.restli.server.annotations.Context;
import com.linkedin.restli.server.annotations.Finder;
import com.linkedin.restli.server.annotations.QueryParam;
import java.util.Collections;
import java.util.Iterator;

import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Inject;
import com.linkedin.restli.common.ComplexResourceKey;
import com.linkedin.restli.common.EmptyRecord;
import com.linkedin.restli.server.annotations.RestLiCollection;
import com.linkedin.restli.server.resources.ComplexKeyResourceTemplate;

import gobblin.metrics.event.TimingEvent;
import gobblin.service.monitoring.FlowStatusGenerator;


/**
 * Resource for handling flow status requests
 */
@RestLiCollection(name = "flowstatuses", namespace = "gobblin.service", keyName = "id")
public class FlowStatusResource extends ComplexKeyResourceTemplate {
  private static final Logger LOG = LoggerFactory.getLogger(FlowStatusResource.class);
  public static final String FLOW_STATUS_GENERATOR_INJECT_NAME = "FlowStatusGenerator";
  public static final String MESSAGE_SEPARATOR = ", ";

  @Inject @javax.inject.Inject @javax.inject.Named(FLOW_STATUS_GENERATOR_INJECT_NAME)
  FlowStatusGenerator _flowStatusGenerator;

  public FlowStatusResource() {}

  /**
   * Retrieve the FlowStatus with the given key
   * @param key flow status id key containing group name and flow name
   * @return {@link FlowStatus} with flow status for the latest execution of the flow
   */
  @Override
  public FlowStatus get(ComplexResourceKey key) {
    String flowGroup = key.getKey().getFlowGroup();
    String flowName = key.getKey().getFlowName();
    long flowExecutionId = key.getKey().getFlowExecutionId();

    LOG.info("Get called with flowGroup " + flowGroup + " flowName " + flowName + " flowExecutionId " + flowExecutionId);

    gobblin.service.monitoring.FlowStatus flowStatus =
        _flowStatusGenerator.getFlowStatus(flowName, flowGroup, flowExecutionId);

    // this returns null to raise a 404 error if flowStatus is null
    return convertFlowStatus(flowStatus);
  }

  @Finder("latestFlowStatus")
  public List getLatestFlowStatus(@Context PagingContext context,
      @QueryParam("flowId") FlowId flowId) {
    LOG.info("getLatestFlowStatus called with flowGroup " + flowId.getFlowGroup() + " flowName " + flowId.getFlowName());

    gobblin.service.monitoring.FlowStatus latestFlowStatus =
        _flowStatusGenerator.getLatestFlowStatus(flowId.getFlowName(), flowId.getFlowGroup());

    if (latestFlowStatus != null) {
      return Collections.singletonList(convertFlowStatus(latestFlowStatus));
    }

    // will return 404 status code
    return null;
  }

  /**
   * Forms a {@link gobblin.service.FlowStatus} from a {@link gobblin.service.monitoring.FlowStatus}
   * @param monitoringFlowStatus
   * @return a {@link gobblin.service.FlowStatus} converted from a {@link gobblin.service.monitoring.FlowStatus}
   */
  private FlowStatus convertFlowStatus(gobblin.service.monitoring.FlowStatus monitoringFlowStatus) {
    if (monitoringFlowStatus == null) {
      return null;
    }

    Iterator jobStatusIter = monitoringFlowStatus.getJobStatusIterator();
    JobStatusArray jobStatusArray = new JobStatusArray();
    FlowId flowId = new FlowId().setFlowName(monitoringFlowStatus.getFlowName())
        .setFlowGroup(monitoringFlowStatus.getFlowGroup());
    long flowStartTime = Long.MAX_VALUE;
    long flowEndTime = -1L;
    // flow execution status is complete unless job status indicates it is running or failed
    ExecutionStatus flowExecutionStatus = ExecutionStatus.COMPLETE;
    StringBuffer flowMessagesStringBuffer = new StringBuffer();

    while (jobStatusIter.hasNext()) {
      gobblin.service.monitoring.JobStatus queriedJobStatus = jobStatusIter.next();
      JobStatus jobStatus = new JobStatus();

      jobStatus.setFlowId(flowId)
          .setJobId(new JobId().setJobName(queriedJobStatus.getJobName())
              .setJobGroup(queriedJobStatus.getJobGroup()))
          .setExecutionStatistics(new JobStatistics()
              .setExecutionStartTime(queriedJobStatus.getStartTime())
              .setExecutionEndTime(queriedJobStatus.getEndTime())
              .setProcessedCount(queriedJobStatus.getProcessedCount()))
          .setExecutionStatus(timingEventToStatus(queriedJobStatus.getEventName()))
          .setMessage(queriedJobStatus.getMessage())
          .setJobState(new JobState().setLowWatermark(queriedJobStatus.getLowWatermark()).
              setHighWatermark(queriedJobStatus.getHighWatermark()));

      jobStatusArray.add(jobStatus);

      if (queriedJobStatus.getStartTime() < flowStartTime){
        flowStartTime = queriedJobStatus.getStartTime();
      }

      // TODO: end time should be left as -1 if not all jobs have started for the flow
      // need to have flow job count to determine this
      if (queriedJobStatus.getEndTime() > flowEndTime){
        flowEndTime = queriedJobStatus.getEndTime();
      }

      if (!queriedJobStatus.getMessage().isEmpty()) {
        flowMessagesStringBuffer.append(queriedJobStatus.getMessage());
        flowMessagesStringBuffer.append(MESSAGE_SEPARATOR);
      }

      flowExecutionStatus = updatedFlowExecutionStatus(jobStatus.getExecutionStatus(), flowExecutionStatus);
    }

    String flowMessages = flowMessagesStringBuffer.length() > 0 ?
        flowMessagesStringBuffer.substring(0, flowMessagesStringBuffer.length() -
            MESSAGE_SEPARATOR.length()) : StringUtils.EMPTY;

    return new FlowStatus()
        .setId(new FlowStatusId().setFlowGroup(flowId.getFlowGroup()).setFlowName(flowId.getFlowName())
            .setFlowExecutionId(monitoringFlowStatus.getFlowExecutionId()))
        .setExecutionStatistics(new FlowStatistics().setExecutionStartTime(flowStartTime)
            .setExecutionEndTime(flowEndTime))
        .setMessage(flowMessages)
        .setExecutionStatus(flowExecutionStatus)
        .setJobStatuses(jobStatusArray);
  }

  /**
   * Maps a timing event name to a flow/job ExecutionStatus
   * @param timingEvent timing event name
   * @return status string
   */
  private ExecutionStatus timingEventToStatus(String timingEvent) {
    ExecutionStatus status;

    switch (timingEvent) {
      case TimingEvent.LauncherTimings.JOB_FAILED:
      case TimingEvent.LauncherTimings.JOB_CANCEL:
        status = ExecutionStatus.FAILED;
        break;
      case TimingEvent.LauncherTimings.JOB_COMPLETE:
        status = ExecutionStatus.COMPLETE;
        break;
      default:
        status = ExecutionStatus.RUNNING;
    }

    return status;
  }

  /**
   * Determines the new flow status based on the current flow status and new job status
   * @param jobExecutionStatus job status
   * @param currentFlowExecutionStatus current flow status
   * @return updated flow status
   */
  private ExecutionStatus updatedFlowExecutionStatus(ExecutionStatus jobExecutionStatus,
      ExecutionStatus currentFlowExecutionStatus) {

    // if any job failed or flow has failed then return failed status
    if (currentFlowExecutionStatus == ExecutionStatus.FAILED ||
        jobExecutionStatus == ExecutionStatus.FAILED) {
      return ExecutionStatus.FAILED;
    }

    // if job still running then flow is still running
    if (jobExecutionStatus == ExecutionStatus.RUNNING) {
      return ExecutionStatus.RUNNING;
    }

    return currentFlowExecutionStatus;
  }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy