/*
* Copyright 2014 TWO SIGMA OPEN SOURCE, LLC
*
* 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 com.twosigma.beaker.jvm.object;
import com.google.inject.Singleton;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.logging.Logger;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
/**
* The SparkProgressService reports the progress of tasks and stages
* during a running Spark job.
*/
@Singleton
public class SparkProgressService {
private final static Logger logger = Logger.getLogger(SparkProgressService.class.getName());
private volatile SparkProgress progress = new SparkProgress();
private int activeJobId;
private String activeAppName;
private Map<Integer, List<Long>> activeTasks = new HashMap<>();
private Map<Integer, List<Long>> failedTasks = new HashMap<>();
private Map<Integer, List<Long>> succeededTasks = new HashMap<>();
private Map<Integer, StageProgress> stages = new HashMap<>();
private List<Integer> jobs = new ArrayList<>();
private Map<Integer, List<Integer>> stagesPerJob = new HashMap<>();
private List<String> executorIds = new ArrayList<String>();
public SparkProgress getProgress() {
return this.progress;
}
public void clear() {
this.progress.clear();
this.jobs.clear();
this.stagesPerJob.clear();
this.activeTasks.clear();
this.failedTasks.clear();
this.succeededTasks.clear();
this.stages.clear();
}
public void jobStart(int jobId, List<String> executorIds) {
activeJobId = jobId;
if (!jobs.contains(jobId)) {
jobs.add(jobId);
stagesPerJob.put(jobId, new ArrayList<Integer>());
}
if (!executorIds.isEmpty()) {
this.executorIds.clear();
this.executorIds.addAll(executorIds);
}
}
public void jobEnd(int jobId, List<String> executorIds) {
if (jobId != activeJobId)
logger.warning(String.format("Spark job %d was not registered as active.", jobId));
if (!executorIds.isEmpty()) {
this.executorIds.clear();
this.executorIds.addAll(executorIds);
}
}
public void stageStart(int stageId, int numTasks) {
if (stages.containsKey(stageId))
logger.warning(String.format("Spark stage %d already exists and will be started.", stageId));
StageProgress sp = new StageProgress();
sp.setStageId(stageId);
sp.setRunning(true);
sp.setTotalTasks(numTasks);
stages.put(stageId, sp);
List<Integer> sts = stagesPerJob.getOrDefault(activeJobId, new ArrayList<Integer>());
sts.add(stageId);
stagesPerJob.put(activeJobId, sts);
activeTasks.put(stageId, new ArrayList<Long>());
failedTasks.put(stageId, new ArrayList<Long>());
succeededTasks.put(stageId, new ArrayList<Long>());
}
public void stageEnd(int stageId, String failureReason) {
if (!stages.containsKey(stageId))
logger.warning(String.format("Spark stage %d could not be found for stage progress reporting.", stageId));
StageProgress sp = stages.getOrDefault(stageId, new StageProgress());
sp.setStageId(stageId);
sp.setRunning(false);
sp.setFailureReason(failureReason);
stages.put(stageId, sp);
}
private void removeTask(int stageId, long taskId) {
List<Long> at = activeTasks.getOrDefault(stageId, new ArrayList<Long>());
if (at.contains(taskId)) {
at.remove(taskId);
activeTasks.put(stageId, at);
}
List<Long> ft = failedTasks.getOrDefault(stageId, new ArrayList<Long>());
if (ft.contains(taskId)) {
ft.remove(taskId);
failedTasks.put(stageId, ft);
}
List<Long> st = succeededTasks.getOrDefault(stageId, new ArrayList<Long>());
if (st.contains(taskId)) {
st.remove(taskId);
succeededTasks.put(stageId, st);
}
}
public void taskStart(int stageId, long taskId) {
if (!stages.containsKey(stageId)) {
logger.warning(String.format("Spark stage %d could not be found for task progress reporting.", stageId));
return;
}
removeTask(stageId, taskId);
List<Long> at = activeTasks.getOrDefault(stageId, new ArrayList<Long>());
at.add(taskId);
activeTasks.put(stageId, at);
}
public void taskEnd(int stageId, long taskId, boolean failed) {
if (!stages.containsKey(stageId)) {
logger.warning(String.format("Spark stage %d could not be found for task progress reporting.", stageId));
return;
}
removeTask(stageId, taskId);
if (failed) {
List<Long> ft = failedTasks.getOrDefault(stageId, new ArrayList<Long>());
ft.add(taskId);
failedTasks.put(stageId, ft);
} else {
List<Long> st = succeededTasks.getOrDefault(stageId, new ArrayList<Long>());
st.add(taskId);
succeededTasks.put(stageId, st);
}
}
/*
* Classes for JSON serialization
*/
@JsonAutoDetect
public static class ApplicationProgress {
private String appName;
private boolean running;
public boolean isRunning() {
return running;
}
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName = appName;
}
public void setRunnning(boolean running) {
this.running = running;
}
public ApplicationProgress() {
}
public ApplicationProgress(
String appName,
boolean running) {
this.appName = appName;
this.running = running;
}
}
@JsonAutoDetect
public static class JobProgress {
private int id;
private boolean running;
private ArrayList<StageProgress> stages;
private ArrayList<String> executorIds;
public boolean isRunning() {
return running;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public void setRunning(boolean running) {
this.running = running;
}
public ArrayList<StageProgress> getStages() {
return this.stages;
}
public void setStages(ArrayList<StageProgress> stages) {
this.stages = stages;
}
public ArrayList<String> getExecutorIds() {
return this.executorIds;
}
public void setExecutorIds(ArrayList<String> executorIds) {
this.executorIds = executorIds;
}
public JobProgress() {
this.stages = new ArrayList<StageProgress>();
this.executorIds = new ArrayList<String>();
}
public JobProgress(
int id,
boolean running) {
this.id = id;
this.running = running;
this.stages = new ArrayList<StageProgress>();
this.executorIds = new ArrayList<String>();
}
}
@JsonAutoDetect
public static class StageProgress {
private int stageId;
private String failureReason;
private int totalTasks;
private int succeededTasks;
private int failedTasks;
private int activeTasks;
private boolean running;
public boolean hasFailed() {
return failureReason != null && !failureReason.isEmpty();
}
public String getFailureReason() {
return failureReason;
}
public int getStageId() {
return stageId;
}
public int getTotalTasks() {
return totalTasks;
}
public int getSucceededTasks() {
return succeededTasks;
}
public int getFailedTasks() {
return failedTasks;
}
public int getActiveTasks() {
return activeTasks;
}
public boolean isRunning() {
return running;
}
public void setFailureReason(String failureReason) {
this.failureReason = failureReason;
}
public void setStageId(int stageId) {
this.stageId = stageId;
}
public void setTotalTasks(int totalTasks) {
this.totalTasks = totalTasks;
}
public void setSucceededTasks(int succeededTasks) {
this.succeededTasks = succeededTasks;
}
public void setFailedTasks(int failedTasks) {
this.failedTasks = failedTasks;
}
public void setActiveTasks(int activeTasks) {
this.activeTasks = activeTasks;
}
public void setRunning(boolean running) {
this.running = running;
}
public StageProgress() {
this.failureReason = null;
}
public StageProgress(
String failureReason,
int stageId,
int totalTasks,
int succeededTasks,
int failedTasks,
int activeTasks,
boolean running) {
this.failureReason = failureReason;
this.stageId = stageId;
this.totalTasks = totalTasks;
this.succeededTasks = succeededTasks;
this.failedTasks = failedTasks;
this.activeTasks = activeTasks;
this.running = running;
}
}
@JsonAutoDetect
public static class SparkProgress {
private List<JobProgress> jobs;
private List<String> executorIds;
public List<JobProgress> getJobs() {
return this.jobs;
}
public void setJobs(List<JobProgress> jobs) {
this.jobs = jobs;
}
public List<String> getExecutorIds() {
return this.executorIds;
}
public void setExecutorIds(List<String> executorIds) {
this.executorIds.clear();
this.executorIds.addAll(executorIds);
}
public void clear() {
this.jobs.clear();
this.executorIds.clear();
this.executorIds.add("Cleared progress");
}
public SparkProgress() {
this.jobs = new ArrayList<>();
this.executorIds = new ArrayList<>();
}
public SparkProgress(
List<JobProgress> jobs,
List<String> executorIds) {
this.jobs = jobs;
this.executorIds = executorIds;
}
}
}