org.apache.ambari.log4j.hadoop.mapreduce.jobhistory.MapReduceJobHistoryUpdater.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.ambari.log4j.hadoop.mapreduce.jobhistory.MapReduceJobHistoryUpdater.java

Source

/**
 * 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 org.apache.ambari.log4j.hadoop.mapreduce.jobhistory;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.ambari.eventdb.model.WorkflowContext;
import org.apache.ambari.eventdb.model.WorkflowDag;
import org.apache.ambari.eventdb.model.WorkflowDag.WorkflowDagEntry;
import org.apache.ambari.log4j.common.LogStoreUpdateProvider;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.mapreduce.Counter;
import org.apache.hadoop.mapreduce.CounterGroup;
import org.apache.hadoop.mapreduce.Counters;
import org.apache.hadoop.mapreduce.TaskType;
import org.apache.hadoop.tools.rumen.HistoryEvent;
import org.apache.hadoop.tools.rumen.JhCounter;
import org.apache.hadoop.tools.rumen.JhCounterGroup;
import org.apache.hadoop.tools.rumen.JhCounters;
import org.apache.hadoop.tools.rumen.JobFinishedEvent;
import org.apache.hadoop.tools.rumen.JobInfoChangeEvent;
import org.apache.hadoop.tools.rumen.JobInitedEvent;
import org.apache.hadoop.tools.rumen.JobStatusChangedEvent;
import org.apache.hadoop.tools.rumen.JobSubmittedEvent;
import org.apache.hadoop.tools.rumen.JobUnsuccessfulCompletionEvent;
import org.apache.hadoop.tools.rumen.MapAttemptFinishedEvent;
import org.apache.hadoop.tools.rumen.ReduceAttemptFinishedEvent;
import org.apache.hadoop.tools.rumen.TaskAttemptFinishedEvent;
import org.apache.hadoop.tools.rumen.TaskAttemptStartedEvent;
import org.apache.hadoop.tools.rumen.TaskAttemptUnsuccessfulCompletionEvent;
import org.apache.hadoop.tools.rumen.TaskFailedEvent;
import org.apache.hadoop.tools.rumen.TaskFinishedEvent;
import org.apache.hadoop.tools.rumen.TaskStartedEvent;
import org.apache.hadoop.util.StringUtils;
import org.apache.log4j.spi.LoggingEvent;
import org.codehaus.jackson.map.ObjectMapper;

public class MapReduceJobHistoryUpdater implements LogStoreUpdateProvider {

    private static final Log LOG = LogFactory.getLog(MapReduceJobHistoryUpdater.class);

    private Connection connection;

    private static final String WORKFLOW_TABLE = "workflow";
    private static final String JOB_TABLE = "job";
    private static final String TASK_TABLE = "task";
    private static final String TASKATTEMPT_TABLE = "taskAttempt";

    private PreparedStatement workflowPS = null;
    private PreparedStatement workflowSelectPS = null;
    private PreparedStatement workflowUpdateTimePS = null;
    private PreparedStatement workflowUpdateNumCompletedPS = null;

    private Map<Class<? extends HistoryEvent>, PreparedStatement> entitySqlMap = new HashMap<Class<? extends HistoryEvent>, PreparedStatement>();

    @Override
    public void init(Connection connection) throws IOException {
        this.connection = connection;

        try {
            initializePreparedStatements();
        } catch (SQLException sqle) {
            throw new IOException(sqle);
        }
    }

    private void initializePreparedStatements() throws SQLException {
        initializeJobPreparedStatements();
        initializeTaskPreparedStatements();
        initializeTaskAttemptPreparedStatements();
    }

    private PreparedStatement jobEndUpdate;

    private void initializeJobPreparedStatements() throws SQLException {

        /** 
         * Job events
         */

        // JobSubmittedEvent

        PreparedStatement jobSubmittedPrepStmnt = connection.prepareStatement("INSERT INTO " + JOB_TABLE + " ("
                + "jobId, " + "jobName, " + "userName, " + "confPath, " + "queue, " + "submitTime, "
                + "workflowId, " + "workflowEntityName " + ") " + "VALUES" + " (?, ?, ?, ?, ?, ?, ?, ?)");
        entitySqlMap.put(JobSubmittedEvent.class, jobSubmittedPrepStmnt);

        workflowSelectPS = connection
                .prepareStatement("SELECT workflowContext FROM " + WORKFLOW_TABLE + " where workflowId = ?");

        workflowPS = connection
                .prepareStatement("INSERT INTO " + WORKFLOW_TABLE + " (" + "workflowId, " + "workflowName, "
                        + "workflowContext, " + "userName, " + "startTime, " + "lastUpdateTime, " + "duration, "
                        + "numJobsTotal, " + "numJobsCompleted" + ") " + "VALUES" + " (?, ?, ?, ?, ?, ?, 0, ?, 0)");

        workflowUpdateTimePS = connection.prepareStatement(
                "UPDATE " + WORKFLOW_TABLE + " SET " + "workflowContext = ?, " + "numJobsTotal = ?, "
                        + "lastUpdateTime = ?, " + "duration = ? - startTime " + "WHERE workflowId = ?");

        workflowUpdateNumCompletedPS = connection.prepareStatement("UPDATE " + WORKFLOW_TABLE + " SET "
                + "lastUpdateTime = ?, " + "duration = ? - startTime, " + "numJobsCompleted = (" + "SELECT count(*)"
                + " FROM " + JOB_TABLE + " WHERE " + "workflowId = " + WORKFLOW_TABLE + ".workflowId"
                + " AND status = 'SUCCESS'), " + "inputBytes = (" + "SELECT sum(inputBytes)" + " FROM " + JOB_TABLE
                + " WHERE " + "workflowId = " + WORKFLOW_TABLE + ".workflowId" + " AND status = 'SUCCESS'), "
                + "outputBytes = (" + "SELECT sum(outputBytes)" + " FROM " + JOB_TABLE + " WHERE " + "workflowId = "
                + WORKFLOW_TABLE + ".workflowId" + " AND status = 'SUCCESS') "
                + " WHERE workflowId = (SELECT workflowId FROM " + JOB_TABLE + " WHERE jobId = ?)");

        // JobFinishedEvent

        PreparedStatement jobFinishedPrepStmnt = connection.prepareStatement("UPDATE " + JOB_TABLE + " SET "
                + "finishTime = ?, " + "finishedMaps = ?, " + "finishedReduces= ?, " + "failedMaps = ?, "
                + "failedReduces = ?, " + "inputBytes = ?, " + "outputBytes = ? " + "WHERE " + "jobId = ?");
        entitySqlMap.put(JobFinishedEvent.class, jobFinishedPrepStmnt);

        // JobInitedEvent

        PreparedStatement jobInitedPrepStmnt = connection.prepareStatement("UPDATE " + JOB_TABLE + " SET "
                + "launchTime = ?, " + "maps = ?, " + "reduces = ?, " + "status = ? " + "WHERE " + "jobId = ?");
        entitySqlMap.put(JobInitedEvent.class, jobInitedPrepStmnt);

        // JobStatusChangedEvent

        PreparedStatement jobStatusChangedPrepStmnt = connection
                .prepareStatement("UPDATE " + JOB_TABLE + " SET " + "status = ? " + "WHERE " + "jobId = ?");
        entitySqlMap.put(JobStatusChangedEvent.class, jobStatusChangedPrepStmnt);

        // JobInfoChangedEvent

        PreparedStatement jobInfoChangedPrepStmnt = connection.prepareStatement(
                "UPDATE " + JOB_TABLE + " SET " + "submitTime = ?, " + "launchTime = ? " + "WHERE " + "jobId = ?");
        entitySqlMap.put(JobInfoChangeEvent.class, jobInfoChangedPrepStmnt);

        // JobUnsuccessfulCompletionEvent
        PreparedStatement jobUnsuccessfulPrepStmnt = connection
                .prepareStatement("UPDATE " + JOB_TABLE + " SET " + "finishTime = ?, " + "finishedMaps = ?, "
                        + "finishedReduces = ?, " + "status = ? " + "WHERE " + "jobId = ?");
        entitySqlMap.put(JobUnsuccessfulCompletionEvent.class, jobUnsuccessfulPrepStmnt);

        // Job update at the end
        jobEndUpdate = connection.prepareStatement("UPDATE " + JOB_TABLE + " SET " + " mapsRuntime = (" + "SELECT "
                + "SUM(" + TASKATTEMPT_TABLE + ".finishTime" + " - " + TASKATTEMPT_TABLE + ".startTime" + ")"
                + " FROM " + TASKATTEMPT_TABLE + " WHERE " + TASKATTEMPT_TABLE + ".jobId = " + JOB_TABLE + ".jobId "
                + " AND " + TASKATTEMPT_TABLE + ".taskType = ?)" + ", " + " reducesRuntime = (" + "SELECT SUM("
                + TASKATTEMPT_TABLE + ".finishTime" + " - " + TASKATTEMPT_TABLE + ".startTime" + ")" + " FROM "
                + TASKATTEMPT_TABLE + " WHERE " + TASKATTEMPT_TABLE + ".jobId = " + JOB_TABLE + ".jobId " + " AND "
                + TASKATTEMPT_TABLE + ".taskType = ?) " + " WHERE " + "jobId = ?");
    }

    private void initializeTaskPreparedStatements() throws SQLException {

        /** 
         * Task events
         */

        // TaskStartedEvent 

        PreparedStatement taskStartedPrepStmnt = connection
                .prepareStatement("INSERT INTO " + TASK_TABLE + " (" + "jobId, " + "taskType, " + "splits, "
                        + "startTime, " + "taskId" + ") " + "VALUES (?, ?, ?, ?, ?)");
        entitySqlMap.put(TaskStartedEvent.class, taskStartedPrepStmnt);

        // TaskFinishedEvent

        PreparedStatement taskFinishedPrepStmnt = connection.prepareStatement("UPDATE " + TASK_TABLE + " SET "
                + "jobId = ?, " + "taskType = ?, " + "status = ?, " + "finishTime = ? " + " WHERE " + "taskId = ?");
        entitySqlMap.put(TaskFinishedEvent.class, taskFinishedPrepStmnt);

        // TaskFailedEvent

        PreparedStatement taskFailedPrepStmnt = connection.prepareStatement(
                "UPDATE " + TASK_TABLE + " SET " + "jobId = ?, " + "taskType = ?, " + "status = ?, "
                        + "finishTime = ?, " + "error = ?, " + "failedAttempt = ? " + "WHERE " + "taskId = ?");
        entitySqlMap.put(TaskFailedEvent.class, taskFailedPrepStmnt);
    }

    private void initializeTaskAttemptPreparedStatements() throws SQLException {

        /**
         * TaskAttempt events
         */

        // TaskAttemptStartedEvent

        PreparedStatement taskAttemptStartedPrepStmnt = connection.prepareStatement("INSERT INTO "
                + TASKATTEMPT_TABLE + " (" + "jobId, " + "taskId, " + "taskType, " + "startTime, " + "taskTracker, "
                + "locality, " + "avataar, " + "taskAttemptId" + ") " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
        entitySqlMap.put(TaskAttemptStartedEvent.class, taskAttemptStartedPrepStmnt);

        // TaskAttemptFinishedEvent

        PreparedStatement taskAttemptFinishedPrepStmnt = connection.prepareStatement("UPDATE " + TASKATTEMPT_TABLE
                + " SET " + "jobId = ?, " + "taskId = ?, " + "taskType = ?, " + "finishTime = ?, " + "status = ?, "
                + "taskTracker = ? " + " WHERE " + "taskAttemptId = ?");
        entitySqlMap.put(TaskAttemptFinishedEvent.class, taskAttemptFinishedPrepStmnt);

        // TaskAttemptUnsuccessfulEvent

        PreparedStatement taskAttemptUnsuccessfulPrepStmnt = connection
                .prepareStatement("UPDATE " + TASKATTEMPT_TABLE + " SET " + "jobId = ?, " + "taskId = ?, "
                        + "taskType = ?, " + "finishTime = ?, " + "status = ?, " + "taskTracker = ?, "
                        + "error = ? " + " WHERE " + "taskAttemptId = ?");
        entitySqlMap.put(TaskAttemptUnsuccessfulCompletionEvent.class, taskAttemptUnsuccessfulPrepStmnt);

        // MapAttemptFinishedEvent

        PreparedStatement mapAttemptFinishedPrepStmnt = connection.prepareStatement(
                "UPDATE " + TASKATTEMPT_TABLE + " SET " + "jobId = ?, " + "taskId = ?, " + "taskType = ?, "
                        + "mapFinishTime = ?, " + "finishTime = ?, " + "inputBytes = ?, " + "outputBytes = ?, "
                        + "status = ?, " + "taskTracker = ? " + " WHERE " + "taskAttemptId = ?");
        entitySqlMap.put(MapAttemptFinishedEvent.class, mapAttemptFinishedPrepStmnt);

        // ReduceAttemptFinishedEvent

        PreparedStatement reduceAttemptFinishedPrepStmnt = connection.prepareStatement("UPDATE " + TASKATTEMPT_TABLE
                + " SET " + "jobId = ?, " + "taskId = ?, " + "taskType = ?, " + "shuffleFinishTime = ?, "
                + "sortFinishTime = ?, " + "finishTime = ?, " + "inputBytes = ?, " + "outputBytes = ?, "
                + "status = ?, " + "taskTracker = ? " + " WHERE " + "taskAttemptId = ?");
        entitySqlMap.put(ReduceAttemptFinishedEvent.class, reduceAttemptFinishedPrepStmnt);
    }

    private void doUpdates(LoggingEvent originalEvent, Object parsedEvent) throws SQLException {
        Class<?> eventClass = parsedEvent.getClass();

        PreparedStatement entityPS = entitySqlMap.get(eventClass);
        if (entityPS == null) {
            LOG.debug("No prepared statement for " + eventClass);
            return;
        }

        if (eventClass == JobSubmittedEvent.class) {
            processJobSubmittedEvent(entityPS, workflowSelectPS, workflowPS, workflowUpdateTimePS, originalEvent,
                    (JobSubmittedEvent) parsedEvent);
        } else if (eventClass == JobFinishedEvent.class) {
            processJobFinishedEvent(entityPS, workflowUpdateNumCompletedPS, originalEvent,
                    (JobFinishedEvent) parsedEvent);
        } else if (eventClass == JobInitedEvent.class) {
            processJobInitedEvent(entityPS, originalEvent, (JobInitedEvent) parsedEvent);
        } else if (eventClass == JobStatusChangedEvent.class) {
            processJobStatusChangedEvent(entityPS, originalEvent, (JobStatusChangedEvent) parsedEvent);
        } else if (eventClass == JobInfoChangeEvent.class) {
            processJobInfoChangeEvent(entityPS, originalEvent, (JobInfoChangeEvent) parsedEvent);
        } else if (eventClass == JobUnsuccessfulCompletionEvent.class) {
            processJobUnsuccessfulEvent(entityPS, originalEvent, (JobUnsuccessfulCompletionEvent) parsedEvent);
        } else if (eventClass == TaskStartedEvent.class) {
            processTaskStartedEvent(entityPS, originalEvent, (TaskStartedEvent) parsedEvent);
        } else if (eventClass == TaskFinishedEvent.class) {
            processTaskFinishedEvent(entityPS, originalEvent, (TaskFinishedEvent) parsedEvent);
        } else if (eventClass == TaskFailedEvent.class) {
            processTaskFailedEvent(entityPS, originalEvent, (TaskFailedEvent) parsedEvent);
        } else if (eventClass == TaskAttemptStartedEvent.class) {
            processTaskAttemptStartedEvent(entityPS, originalEvent, (TaskAttemptStartedEvent) parsedEvent);
        } else if (eventClass == TaskAttemptFinishedEvent.class) {
            processTaskAttemptFinishedEvent(entityPS, originalEvent, (TaskAttemptFinishedEvent) parsedEvent);
        } else if (eventClass == TaskAttemptUnsuccessfulCompletionEvent.class) {
            processTaskAttemptUnsuccessfulEvent(entityPS, originalEvent,
                    (TaskAttemptUnsuccessfulCompletionEvent) parsedEvent);
        } else if (eventClass == MapAttemptFinishedEvent.class) {
            processMapAttemptFinishedEvent(entityPS, originalEvent, (MapAttemptFinishedEvent) parsedEvent);
        } else if (eventClass == ReduceAttemptFinishedEvent.class) {
            processReduceAttemptFinishedEvent(entityPS, originalEvent, (ReduceAttemptFinishedEvent) parsedEvent);
        }
    }

    private void updateJobStatsAtFinish(String jobId) {
        try {
            jobEndUpdate.setString(1, "MAP");
            jobEndUpdate.setString(2, "REDUCE");
            jobEndUpdate.setString(3, jobId);
            jobEndUpdate.executeUpdate();
        } catch (SQLException sqle) {
            LOG.info("Failed to update mapsRuntime/reducesRuntime for " + jobId, sqle);
        }
    }

    private static WorkflowContext generateWorkflowContext(JobSubmittedEvent historyEvent) {
        WorkflowDag wfDag = new WorkflowDag();
        WorkflowDagEntry wfDagEntry = new WorkflowDagEntry();
        wfDagEntry.setSource("X");
        wfDag.addEntry(wfDagEntry);

        WorkflowContext wc = new WorkflowContext();
        wc.setWorkflowId(historyEvent.getJobId().toString().replace("job_", "mr_"));
        wc.setWorkflowName(historyEvent.getJobName());
        wc.setWorkflowEntityName("X");
        wc.setWorkflowDag(wfDag);
        return wc;
    }

    // this is based on the regex in org.apache.hadoop.tools.rumen.ParsedLine
    // except this assumes the format "key"="value" so that both key and value
    // are quoted and may contain escaped characters
    private static final Pattern adjPattern = Pattern
            .compile("\"([^\"\\\\]*+(?:\\\\.[^\"\\\\]*+)*+)\"" + "=" + "\"([^\"\\\\]*+(?:\\\\.[^\"\\\\]*+)*+)\" ");

    public static WorkflowContext buildWorkflowContext(JobSubmittedEvent historyEvent) {
        String workflowId = historyEvent.getWorkflowId().replace("\\", "");
        if (workflowId.isEmpty())
            return generateWorkflowContext(historyEvent);
        String workflowName = historyEvent.getWorkflowName().replace("\\", "");
        String workflowNodeName = historyEvent.getWorkflowNodeName().replace("\\", "");
        String workflowAdjacencies = StringUtils.unEscapeString(historyEvent.getWorkflowAdjacencies(),
                StringUtils.ESCAPE_CHAR, new char[] { '"', '=', '.' });
        WorkflowContext context = new WorkflowContext();
        context.setWorkflowId(workflowId);
        context.setWorkflowName(workflowName);
        context.setWorkflowEntityName(workflowNodeName);
        WorkflowDag dag = new WorkflowDag();
        Matcher matcher = adjPattern.matcher(workflowAdjacencies);

        while (matcher.find()) {
            WorkflowDagEntry dagEntry = new WorkflowDagEntry();
            dagEntry.setSource(matcher.group(1).replace("\\", ""));
            String[] values = StringUtils.getStrings(matcher.group(2).replace("\\", ""));
            if (values != null) {
                for (String target : values) {
                    dagEntry.addTarget(target);
                }
            }
            dag.addEntry(dagEntry);
        }
        if (dag.getEntries().isEmpty()) {
            WorkflowDagEntry wfDagEntry = new WorkflowDagEntry();
            wfDagEntry.setSource(workflowNodeName);
            dag.addEntry(wfDagEntry);
        }
        context.setWorkflowDag(dag);
        return context;
    }

    public static void mergeEntries(Map<String, Set<String>> edges, List<WorkflowDagEntry> entries) {
        if (entries == null)
            return;
        for (WorkflowDagEntry entry : entries) {
            if (!edges.containsKey(entry.getSource()))
                edges.put(entry.getSource(), new TreeSet<String>());
            Set<String> targets = edges.get(entry.getSource());
            targets.addAll(entry.getTargets());
        }
    }

    public static WorkflowDag constructMergedDag(WorkflowContext workflowContext,
            WorkflowContext existingWorkflowContext) {
        Map<String, Set<String>> edges = new TreeMap<String, Set<String>>();
        if (existingWorkflowContext.getWorkflowDag() != null)
            mergeEntries(edges, existingWorkflowContext.getWorkflowDag().getEntries());
        if (workflowContext.getWorkflowDag() != null)
            mergeEntries(edges, workflowContext.getWorkflowDag().getEntries());
        WorkflowDag mergedDag = new WorkflowDag();
        for (Entry<String, Set<String>> edge : edges.entrySet()) {
            WorkflowDagEntry entry = new WorkflowDagEntry();
            entry.setSource(edge.getKey());
            entry.getTargets().addAll(edge.getValue());
            mergedDag.addEntry(entry);
        }
        return mergedDag;
    }

    private static WorkflowContext getSanitizedWorkflow(WorkflowContext workflowContext,
            WorkflowContext existingWorkflowContext) {
        WorkflowContext sanitizedWC = new WorkflowContext();
        if (existingWorkflowContext == null) {
            sanitizedWC.setWorkflowDag(workflowContext.getWorkflowDag());
            sanitizedWC.setParentWorkflowContext(workflowContext.getParentWorkflowContext());
        } else {
            sanitizedWC.setWorkflowDag(constructMergedDag(existingWorkflowContext, workflowContext));
            sanitizedWC.setParentWorkflowContext(existingWorkflowContext.getParentWorkflowContext());
        }
        return sanitizedWC;
    }

    private static String getWorkflowString(WorkflowContext sanitizedWC) {
        String sanitizedWCString = null;
        try {
            ObjectMapper om = new ObjectMapper();
            sanitizedWCString = om.writeValueAsString(sanitizedWC);
        } catch (IOException e) {
            e.printStackTrace();
            sanitizedWCString = "";
        }
        return sanitizedWCString;
    }

    private void processJobSubmittedEvent(PreparedStatement jobPS, PreparedStatement workflowSelectPS,
            PreparedStatement workflowPS, PreparedStatement workflowUpdateTimePS, LoggingEvent logEvent,
            JobSubmittedEvent historyEvent) {

        try {
            String jobId = historyEvent.getJobId().toString();
            jobPS.setString(1, jobId);
            jobPS.setString(2, historyEvent.getJobName());
            jobPS.setString(3, historyEvent.getUserName());
            jobPS.setString(4, historyEvent.getJobConfPath());
            jobPS.setString(5, historyEvent.getJobQueueName());
            jobPS.setLong(6, historyEvent.getSubmitTime());

            WorkflowContext workflowContext = buildWorkflowContext(historyEvent);

            // Get workflow information
            boolean insertWorkflow = false;
            String existingContextString = null;

            ResultSet rs = null;
            try {
                workflowSelectPS.setString(1, workflowContext.getWorkflowId());
                workflowSelectPS.execute();
                rs = workflowSelectPS.getResultSet();
                if (rs.next()) {
                    existingContextString = rs.getString(1);
                } else {
                    insertWorkflow = true;
                }
            } catch (SQLException sqle) {
                LOG.warn("workflow select failed with: ", sqle);
                insertWorkflow = false;
            } finally {
                try {
                    if (rs != null)
                        rs.close();
                } catch (SQLException e) {
                    LOG.error("Exception while closing ResultSet", e);
                }
            }

            // Insert workflow 
            if (insertWorkflow) {
                workflowPS.setString(1, workflowContext.getWorkflowId());
                workflowPS.setString(2, workflowContext.getWorkflowName());
                workflowPS.setString(3, getWorkflowString(getSanitizedWorkflow(workflowContext, null)));
                workflowPS.setString(4, historyEvent.getUserName());
                workflowPS.setLong(5, historyEvent.getSubmitTime());
                workflowPS.setLong(6, historyEvent.getSubmitTime());
                workflowPS.setLong(7, workflowContext.getWorkflowDag().size());
                workflowPS.executeUpdate();
                LOG.debug("Successfully inserted workflowId = " + workflowContext.getWorkflowId());
            } else {
                ObjectMapper om = new ObjectMapper();
                WorkflowContext existingWorkflowContext = null;
                try {
                    if (existingContextString != null)
                        existingWorkflowContext = om.readValue(existingContextString.getBytes(),
                                WorkflowContext.class);
                } catch (IOException e) {
                    LOG.warn("Couldn't read existing workflow context for " + workflowContext.getWorkflowId(), e);
                }

                WorkflowContext sanitizedWC = getSanitizedWorkflow(workflowContext, existingWorkflowContext);
                workflowUpdateTimePS.setString(1, getWorkflowString(sanitizedWC));
                workflowUpdateTimePS.setLong(2, sanitizedWC.getWorkflowDag().size());
                workflowUpdateTimePS.setLong(3, historyEvent.getSubmitTime());
                workflowUpdateTimePS.setLong(4, historyEvent.getSubmitTime());
                workflowUpdateTimePS.setString(5, workflowContext.getWorkflowId());
                workflowUpdateTimePS.executeUpdate();
                LOG.debug("Successfully updated workflowId = " + workflowContext.getWorkflowId());
            }

            // Insert job
            jobPS.setString(7, workflowContext.getWorkflowId());
            jobPS.setString(8, workflowContext.getWorkflowEntityName());
            jobPS.executeUpdate();
            LOG.debug("Successfully inserted job = " + jobId + " and workflowId = "
                    + workflowContext.getWorkflowId());

        } catch (SQLException sqle) {
            LOG.info("Failed to store " + historyEvent.getEventType() + " for job " + historyEvent.getJobId()
                    + " into " + JOB_TABLE, sqle);
        } catch (Exception e) {
            LOG.info("Failed to store " + historyEvent.getEventType() + " for job " + historyEvent.getJobId()
                    + " into " + JOB_TABLE, e);
        }
    }

    private void processJobFinishedEvent(PreparedStatement entityPS, PreparedStatement workflowUpdateNumCompletedPS,
            LoggingEvent logEvent, JobFinishedEvent historyEvent) {
        Counters counters = historyEvent.getMapCounters();
        long inputBytes = 0;
        if (counters != null) {
            for (CounterGroup group : counters) {
                for (Counter counter : group) {
                    if (counter.getName().equals("HDFS_BYTES_READ"))
                        inputBytes += counter.getValue();
                }
            }
        }
        if (historyEvent.getFinishedReduces() != 0)
            counters = historyEvent.getReduceCounters();
        long outputBytes = 0;
        if (counters != null) {
            for (CounterGroup group : counters) {
                for (Counter counter : group) {
                    if (counter.getName().equals("HDFS_BYTES_WRITTEN"))
                        outputBytes += counter.getValue();
                }
            }
        }
        try {
            entityPS.setLong(1, historyEvent.getFinishTime());
            entityPS.setInt(2, historyEvent.getFinishedMaps());
            entityPS.setInt(3, historyEvent.getFinishedReduces());
            entityPS.setInt(4, historyEvent.getFailedMaps());
            entityPS.setInt(5, historyEvent.getFailedReduces());
            entityPS.setLong(6, inputBytes);
            entityPS.setLong(7, outputBytes);
            entityPS.setString(8, historyEvent.getJobid().toString());
            entityPS.executeUpdate();
            // job finished events always have success status
            workflowUpdateNumCompletedPS.setLong(1, historyEvent.getFinishTime());
            workflowUpdateNumCompletedPS.setLong(2, historyEvent.getFinishTime());
            workflowUpdateNumCompletedPS.setString(3, historyEvent.getJobid().toString());
            workflowUpdateNumCompletedPS.executeUpdate();
        } catch (SQLException sqle) {
            LOG.info("Failed to store " + historyEvent.getEventType() + " for job " + historyEvent.getJobid()
                    + " into " + JOB_TABLE, sqle);
        }

        updateJobStatsAtFinish(historyEvent.getJobid().toString());

    }

    private void processJobInitedEvent(PreparedStatement entityPS, LoggingEvent logEvent,
            JobInitedEvent historyEvent) {
        try {
            entityPS.setLong(1, historyEvent.getLaunchTime());
            entityPS.setInt(2, historyEvent.getTotalMaps());
            entityPS.setInt(3, historyEvent.getTotalReduces());
            entityPS.setString(4, historyEvent.getStatus());
            entityPS.setString(5, historyEvent.getJobId().toString());
            entityPS.executeUpdate();
        } catch (SQLException sqle) {
            LOG.info("Failed to store " + historyEvent.getEventType() + " for job " + historyEvent.getJobId()
                    + " into " + JOB_TABLE, sqle);
        }
    }

    private void processJobStatusChangedEvent(PreparedStatement entityPS, LoggingEvent logEvent,
            JobStatusChangedEvent historyEvent) {
        try {
            entityPS.setString(1, historyEvent.getStatus());
            entityPS.setString(2, historyEvent.getJobId().toString());
            entityPS.executeUpdate();
        } catch (SQLException sqle) {
            LOG.info("Failed to store " + historyEvent.getEventType() + " for job " + historyEvent.getJobId()
                    + " into " + JOB_TABLE, sqle);
        }
    }

    private void processJobInfoChangeEvent(PreparedStatement entityPS, LoggingEvent logEvent,
            JobInfoChangeEvent historyEvent) {
        try {
            entityPS.setLong(1, historyEvent.getSubmitTime());
            entityPS.setLong(2, historyEvent.getLaunchTime());
            entityPS.setString(3, historyEvent.getJobId().toString());
            entityPS.executeUpdate();
        } catch (SQLException sqle) {
            LOG.info("Failed to store " + historyEvent.getEventType() + " for job " + historyEvent.getJobId()
                    + " into " + JOB_TABLE, sqle);
        }
    }

    private void processJobUnsuccessfulEvent(PreparedStatement entityPS, LoggingEvent logEvent,
            JobUnsuccessfulCompletionEvent historyEvent) {
        try {
            entityPS.setLong(1, historyEvent.getFinishTime());
            entityPS.setLong(2, historyEvent.getFinishedMaps());
            entityPS.setLong(3, historyEvent.getFinishedReduces());
            entityPS.setString(4, historyEvent.getStatus());
            entityPS.setString(5, historyEvent.getJobId().toString());
            entityPS.executeUpdate();
        } catch (SQLException sqle) {
            LOG.info("Failed to store " + historyEvent.getEventType() + " for job " + historyEvent.getJobId()
                    + " into " + JOB_TABLE, sqle);
        }

        updateJobStatsAtFinish(historyEvent.getJobId().toString());
    }

    private void processTaskStartedEvent(PreparedStatement entityPS, LoggingEvent logEvent,
            TaskStartedEvent historyEvent) {
        try {
            entityPS.setString(1, historyEvent.getTaskId().getJobID().toString());
            entityPS.setString(2, historyEvent.getTaskType().toString());
            entityPS.setString(3, historyEvent.getSplitLocations());
            entityPS.setLong(4, historyEvent.getStartTime());
            entityPS.setString(5, historyEvent.getTaskId().toString());
            entityPS.executeUpdate();
        } catch (SQLException sqle) {
            LOG.info("Failed to store " + historyEvent.getEventType() + " for task " + historyEvent.getTaskId()
                    + " into " + TASK_TABLE, sqle);
        }
    }

    private void processTaskFinishedEvent(PreparedStatement entityPS, LoggingEvent logEvent,
            TaskFinishedEvent historyEvent) {
        try {
            entityPS.setString(1, historyEvent.getTaskId().getJobID().toString());
            entityPS.setString(2, historyEvent.getTaskType().toString());
            entityPS.setString(3, historyEvent.getTaskStatus());
            entityPS.setLong(4, historyEvent.getFinishTime());
            entityPS.setString(5, historyEvent.getTaskId().toString());
            entityPS.executeUpdate();
        } catch (SQLException sqle) {
            LOG.info("Failed to store " + historyEvent.getEventType() + " for task " + historyEvent.getTaskId()
                    + " into " + TASK_TABLE, sqle);
        }
    }

    private void processTaskFailedEvent(PreparedStatement entityPS, LoggingEvent logEvent,
            TaskFailedEvent historyEvent) {
        try {
            entityPS.setString(1, historyEvent.getTaskId().getJobID().toString());
            entityPS.setString(2, historyEvent.getTaskType().toString());
            entityPS.setString(3, historyEvent.getTaskStatus());
            entityPS.setLong(4, historyEvent.getFinishTime());
            entityPS.setString(5, historyEvent.getError());
            if (historyEvent.getFailedAttemptID() != null) {
                entityPS.setString(6, historyEvent.getFailedAttemptID().toString());
            } else {
                entityPS.setString(6, "task_na");
            }
            entityPS.setString(7, historyEvent.getTaskId().toString());
            entityPS.executeUpdate();
        } catch (SQLException sqle) {
            LOG.info("Failed to store " + historyEvent.getEventType() + " for task " + historyEvent.getTaskId()
                    + " into " + TASK_TABLE, sqle);
        }
    }

    private void processTaskAttemptStartedEvent(PreparedStatement entityPS, LoggingEvent logEvent,
            TaskAttemptStartedEvent historyEvent) {
        try {
            entityPS.setString(1, historyEvent.getTaskId().getJobID().toString());
            entityPS.setString(2, historyEvent.getTaskId().toString());
            entityPS.setString(3, historyEvent.getTaskType().toString());
            entityPS.setLong(4, historyEvent.getStartTime());
            entityPS.setString(5, historyEvent.getTrackerName());
            entityPS.setString(6, historyEvent.getLocality().toString());
            entityPS.setString(7, historyEvent.getAvataar().toString());
            entityPS.setString(8, historyEvent.getTaskAttemptId().toString());
            entityPS.executeUpdate();
        } catch (SQLException sqle) {
            LOG.info("Failed to store " + historyEvent.getEventType() + " for taskAttempt "
                    + historyEvent.getTaskAttemptId() + " into " + TASKATTEMPT_TABLE, sqle);
        }
    }

    private void processTaskAttemptFinishedEvent(PreparedStatement entityPS, LoggingEvent logEvent,
            TaskAttemptFinishedEvent historyEvent) {

        if (historyEvent.getTaskType() == TaskType.MAP || historyEvent.getTaskType() == TaskType.REDUCE) {
            LOG.debug("Ignoring TaskAttemptFinishedEvent for " + historyEvent.getTaskType());
            return;
        }

        try {
            entityPS.setString(1, historyEvent.getTaskId().getJobID().toString());
            entityPS.setString(2, historyEvent.getTaskId().toString());
            entityPS.setString(3, historyEvent.getTaskType().toString());
            entityPS.setLong(4, historyEvent.getFinishTime());
            entityPS.setString(5, historyEvent.getTaskStatus());
            entityPS.setString(6, historyEvent.getHostname());
            entityPS.setString(7, historyEvent.getAttemptId().toString());
            entityPS.executeUpdate();
        } catch (SQLException sqle) {
            LOG.info("Failed to store " + historyEvent.getEventType() + " for taskAttempt "
                    + historyEvent.getAttemptId() + " into " + TASKATTEMPT_TABLE, sqle);
        }
    }

    private void processTaskAttemptUnsuccessfulEvent(PreparedStatement entityPS, LoggingEvent logEvent,
            TaskAttemptUnsuccessfulCompletionEvent historyEvent) {
        try {
            entityPS.setString(1, historyEvent.getTaskId().getJobID().toString());
            entityPS.setString(2, historyEvent.getTaskId().toString());
            entityPS.setString(3, historyEvent.getTaskType().toString());
            entityPS.setLong(4, historyEvent.getFinishTime());
            entityPS.setString(5, historyEvent.getTaskStatus());
            entityPS.setString(6, historyEvent.getHostname());
            entityPS.setString(7, historyEvent.getError());
            entityPS.setString(8, historyEvent.getTaskAttemptId().toString());
            entityPS.executeUpdate();
        } catch (SQLException sqle) {
            LOG.info("Failed to store " + historyEvent.getEventType() + " for taskAttempt "
                    + historyEvent.getTaskAttemptId() + " into " + TASKATTEMPT_TABLE, sqle);
        }
    }

    private void processMapAttemptFinishedEvent(PreparedStatement entityPS, LoggingEvent logEvent,
            MapAttemptFinishedEvent historyEvent) {

        if (historyEvent.getTaskType() != TaskType.MAP) {
            LOG.debug("Ignoring MapAttemptFinishedEvent for " + historyEvent.getTaskType());
            return;
        }

        long[] ioBytes = getInputOutputBytes(historyEvent.getCounters());

        try {
            entityPS.setString(1, historyEvent.getTaskId().getJobID().toString());
            entityPS.setString(2, historyEvent.getTaskId().toString());
            entityPS.setString(3, historyEvent.getTaskType().toString());
            entityPS.setLong(4, historyEvent.getMapFinishTime());
            entityPS.setLong(5, historyEvent.getFinishTime());
            entityPS.setLong(6, ioBytes[0]);
            entityPS.setLong(7, ioBytes[1]);
            entityPS.setString(8, historyEvent.getTaskStatus());
            entityPS.setString(9, historyEvent.getHostname());
            entityPS.setString(10, historyEvent.getAttemptId().toString());
            entityPS.executeUpdate();
        } catch (SQLException sqle) {
            LOG.info("Failed to store " + historyEvent.getEventType() + " for taskAttempt "
                    + historyEvent.getAttemptId() + " into " + TASKATTEMPT_TABLE, sqle);
        }
    }

    private void processReduceAttemptFinishedEvent(PreparedStatement entityPS, LoggingEvent logEvent,
            ReduceAttemptFinishedEvent historyEvent) {
        if (historyEvent.getTaskType() != TaskType.REDUCE) {
            LOG.debug("Ignoring ReduceAttemptFinishedEvent for " + historyEvent.getTaskType());
            return;
        }

        long[] ioBytes = getInputOutputBytes(historyEvent.getCounters());

        try {
            entityPS.setString(1, historyEvent.getTaskId().getJobID().toString());
            entityPS.setString(2, historyEvent.getTaskId().toString());
            entityPS.setString(3, historyEvent.getTaskType().toString());
            entityPS.setLong(4, historyEvent.getShuffleFinishTime());
            entityPS.setLong(5, historyEvent.getSortFinishTime());
            entityPS.setLong(6, historyEvent.getFinishTime());
            entityPS.setLong(7, ioBytes[0]);
            entityPS.setLong(8, ioBytes[1]);
            entityPS.setString(9, historyEvent.getTaskStatus());
            entityPS.setString(10, historyEvent.getHostname());
            entityPS.setString(11, historyEvent.getAttemptId().toString());
            entityPS.executeUpdate();
        } catch (SQLException sqle) {
            LOG.info("Failed to store " + historyEvent.getEventType() + " for taskAttempt "
                    + historyEvent.getAttemptId() + " into " + TASKATTEMPT_TABLE, sqle);
        }
    }

    public static long[] getInputOutputBytes(JhCounters counters) {
        long inputBytes = 0;
        long outputBytes = 0;
        if (counters != null) {
            for (JhCounterGroup counterGroup : counters.groups) {
                if (counterGroup.name.equals("FileSystemCounters")) {
                    for (JhCounter counter : counterGroup.counts) {
                        if (counter.name.equals("HDFS_BYTES_READ") || counter.name.equals("FILE_BYTES_READ"))
                            inputBytes += counter.value;
                        else if (counter.name.equals("HDFS_BYTES_WRITTEN")
                                || counter.name.equals("FILE_BYTES_WRITTEN"))
                            outputBytes += counter.value;
                    }
                }
            }
        }
        return new long[] { inputBytes, outputBytes };
    }

    @Override
    public void update(LoggingEvent originalEvent, Object parsedEvent) throws IOException {
        try {
            doUpdates(originalEvent, parsedEvent);
        } catch (SQLException sqle) {
            throw new IOException(sqle);
        }
    }

}