/** * 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.hadoop.tools.rumen; import java.io.Closeable; import java.io.IOException; import java.util.Comparator; import java.util.Iterator; import java.util.PriorityQueue; import java.util.TreeMap; import java.util.TreeSet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class DeskewedJobTraceReader implements Closeable { // underlying engine private final JobTraceReader reader; // configuration variables private final int skewBufferLength; private final boolean abortOnUnfixableSkew; // state variables private long skewMeasurementLatestSubmitTime = Long.MIN_VALUE; private long returnedLatestSubmitTime = Long.MIN_VALUE; private int maxSkewBufferNeeded = 0; // a submit time will NOT be in countedRepeatedSubmitTimesSoFar if // it only occurs once. This situation is represented by having the // time in submitTimesSoFar only. A submit time that occurs twice or more // appears in countedRepeatedSubmitTimesSoFar [with the appropriate range // value] AND submitTimesSoFar private TreeMap<Long, Integer> countedRepeatedSubmitTimesSoFar = new TreeMap<Long, Integer>(); private TreeSet<Long> submitTimesSoFar = new TreeSet<Long>(); private final PriorityQueue<LoggedJob> skewBuffer; static final private Log LOG = LogFactory.getLog(DeskewedJobTraceReader.class); static private class JobComparator implements Comparator<LoggedJob> { @Override public int compare(LoggedJob j1, LoggedJob j2) { return (j1.getSubmitTime() < j2.getSubmitTime()) ? -1 : (j1 .getSubmitTime() == j2.getSubmitTime()) ? 0 : 1; } } /** * Constructor. * * @param reader * the {@link JobTraceReader} that's being protected * @param skewBufferLength * [the number of late jobs that can preced a later out-of-order * earlier job * @throws IOException */ public DeskewedJobTraceReader(JobTraceReader reader, int skewBufferLength, boolean abortOnUnfixableSkew) throws IOException { this.reader = reader; this.skewBufferLength = skewBufferLength; this.abortOnUnfixableSkew = abortOnUnfixableSkew; skewBuffer = new PriorityQueue<LoggedJob>(skewBufferLength + 1, new JobComparator()); fillSkewBuffer(); } public DeskewedJobTraceReader(JobTraceReader reader) throws IOException { this(reader, 0, true); } private LoggedJob rawNextJob() throws IOException { LoggedJob result = reader.getNext(); if ((!abortOnUnfixableSkew || skewBufferLength > 0) && result != null) { long thisTime = result.getSubmitTime(); if (submitTimesSoFar.contains(thisTime)) { Integer myCount = countedRepeatedSubmitTimesSoFar.get(thisTime); countedRepeatedSubmitTimesSoFar.put(thisTime, myCount == null ? 2 : myCount + 1); } else { submitTimesSoFar.add(thisTime); } if (thisTime < skewMeasurementLatestSubmitTime) { Iterator<Long> endCursor = submitTimesSoFar.descendingIterator(); int thisJobNeedsSkew = 0; Long keyNeedingSkew; while (endCursor.hasNext() && (keyNeedingSkew = endCursor.next()) > thisTime) { Integer keyNeedsSkewAmount = countedRepeatedSubmitTimesSoFar.get(keyNeedingSkew); thisJobNeedsSkew += keyNeedsSkewAmount == null ? 1 : keyNeedsSkewAmount; } maxSkewBufferNeeded = Math.max(maxSkewBufferNeeded, thisJobNeedsSkew); } skewMeasurementLatestSubmitTime = Math.max(thisTime, skewMeasurementLatestSubmitTime); } return result; } static class OutOfOrderException extends RuntimeException { static final long serialVersionUID = 1L; public OutOfOrderException(String text) { super(text); } } LoggedJob nextJob() throws IOException, OutOfOrderException { LoggedJob newJob = rawNextJob(); if (newJob != null) { skewBuffer.add(newJob); } LoggedJob result = skewBuffer.poll(); while (result != null && result.getSubmitTime() < returnedLatestSubmitTime) { LOG.error("The current job was submitted earlier than the previous one"); LOG.error("Its jobID is " + result.getJobID()); LOG.error("Its submit time is " + result.getSubmitTime() + ",but the previous one was " + returnedLatestSubmitTime); if (abortOnUnfixableSkew) { throw new OutOfOrderException("Job submit time is " + result.getSubmitTime() + ",but the previous one was " + returnedLatestSubmitTime); } result = rawNextJob(); } if (result != null) { returnedLatestSubmitTime = result.getSubmitTime(); } return result; } private void fillSkewBuffer() throws IOException { for (int i = 0; i < skewBufferLength; ++i) { LoggedJob newJob = rawNextJob(); if (newJob == null) { return; } skewBuffer.add(newJob); } } int neededSkewBufferSize() { return maxSkewBufferNeeded; } @Override public void close() throws IOException { reader.close(); } }