Merge pull request #837 from kutzi/estimated-time

[FIXED JENKINS-18196] Change to the estimated duratrion calculation to prevent performance issues with lazy build loading
This commit is contained in:
Kohsuke Kawaguchi committed 2013-07-16 21:47:25 -07:00
commit 12e9a55c39
5 files changed
+111 -19

No files matched your search

+48 -1
View File
@@ -26,6 +26,7 @@ package hudson.model;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import hudson.EnvVars;
import hudson.Extension;
import hudson.ExtensionPoint;
@@ -67,6 +68,7 @@ import jenkins.security.HexStringConfidentialKey;
import jenkins.util.io.OnMaster;
import net.sf.json.JSONException;
import net.sf.json.JSONObject;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
@@ -87,6 +89,7 @@ import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.interceptor.RequirePOST;
import javax.servlet.ServletException;
import java.awt.*;
import java.io.*;
import java.net.URLEncoder;
@@ -898,8 +901,52 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
return result;
}
/**
* Returns candidate build for calculating the estimated duration of the current run.
*
* Returns the 3 last successful (stable or unstable) builds, if there are any.
* Failing to find 3 of those, it will return up to 3 last unsuccessful builds.
*
* In any case it will not go more than 6 builds into the past to avoid costly build loading.
*/
@SuppressWarnings("unchecked")
protected List<RunT> getEstimatedDurationCandidates() {
List<RunT> candidates = new ArrayList<RunT>(3);
RunT lastSuccessful = (RunT) Permalink.LAST_SUCCESSFUL_BUILD.resolve(this);
int lastSuccessfulNumber = -1;
if (lastSuccessful != null) {
candidates.add(lastSuccessful);
lastSuccessfulNumber = lastSuccessful.getNumber();
}
int i = 0;
RunT r = (RunT) Permalink.LAST_BUILD.resolve(this);
List<RunT> fallbackCandidates = new ArrayList<RunT>(3);
while (r != null && candidates.size() < 3 && i < 6) {
if (!r.isBuilding() && r.getResult() != null && r.getNumber() != lastSuccessfulNumber) {
Result result = r.getResult();
if (result.isBetterOrEqualTo(Result.UNSTABLE)) {
candidates.add(r);
} else if (result.isCompleteBuild()) {
fallbackCandidates.add(r);
}
}
i++;
r = r.getPreviousBuild();
}
while (candidates.size() < 3) {
if (fallbackCandidates.isEmpty())
break;
RunT run = fallbackCandidates.remove(0);
candidates.add(run);
}
return candidates;
}
public long getEstimatedDuration() {
List<RunT> builds = getLastBuildsOverThreshold(3, Result.UNSTABLE);
List<RunT> builds = getEstimatedDurationCandidates();
if(builds.isEmpty()) return -1;
+21 -6
View File
@@ -48,30 +48,30 @@ public final class Result implements Serializable, CustomExportedBean {
/**
* The build had no errors.
*/
public static final Result SUCCESS = new Result("SUCCESS",BallColor.BLUE,0);
public static final Result SUCCESS = new Result("SUCCESS",BallColor.BLUE,0,true);
/**
* The build had some errors but they were not fatal.
* For example, some tests failed.
*/
public static final Result UNSTABLE = new Result("UNSTABLE",BallColor.YELLOW,1);
public static final Result UNSTABLE = new Result("UNSTABLE",BallColor.YELLOW,1,true);
/**
* The build had a fatal error.
*/
public static final Result FAILURE = new Result("FAILURE",BallColor.RED,2);
public static final Result FAILURE = new Result("FAILURE",BallColor.RED,2,true);
/**
* The module was not built.
* <p>
* This status code is used in a multi-stage build (like maven2)
* where a problem in earlier stage prevented later stages from building.
*/
public static final Result NOT_BUILT = new Result("NOT_BUILT",BallColor.NOTBUILT,3);
public static final Result NOT_BUILT = new Result("NOT_BUILT",BallColor.NOTBUILT,3,false);
/**
* The build was manually aborted.
*
* If you are catching {@link InterruptedException} and interpreting it as {@link #ABORTED},
* you should check {@link Executor#abortResult()} instead (starting 1.417.)
*/
public static final Result ABORTED = new Result("ABORTED",BallColor.ABORTED,4);
public static final Result ABORTED = new Result("ABORTED",BallColor.ABORTED,4,false);
private final String name;
@@ -84,11 +84,17 @@ public final class Result implements Serializable, CustomExportedBean {
* Default ball color for this status.
*/
public final BallColor color;
/**
* Is this a complete build - i.e. did it run to the end (not aborted)?
*/
public final boolean completeBuild;
private Result(String name, BallColor color, int ordinal) {
private Result(String name, BallColor color, int ordinal, boolean complete) {
this.name = name;
this.color = color;
this.ordinal = ordinal;
this.completeBuild = complete;
}
/**
@@ -116,6 +122,15 @@ public final class Result implements Serializable, CustomExportedBean {
public boolean isBetterOrEqualTo(Result that) {
return this.ordinal <= that.ordinal;
}
/**
* Is this a complete build - i.e. did it run to the end (not aborted)?
*
* @since TODO
*/
public boolean isCompleteBuild() {
return this.completeBuild;
}
@Override