Simplify `LoadStatistics`

This commit is contained in:
Jesse Glick 2025-04-21 15:40:50 -04:00
parent 88cf6f642e
commit dca328563b
No known key found for this signature in database
GPG Key ID: 1DDA69D94B624311
6 changed files with 15 additions and 167 deletions

View File

@ -109,21 +109,6 @@ public abstract class Label extends Actionable implements Comparable<Label>, Mod
this.name = name;
// passing these causes an infinite loop - getTotalExecutors(),getBusyExecutors());
this.loadStatistics = new LoadStatistics(0, 0) {
@Override
public int computeIdleExecutors() {
return Label.this.getIdleExecutors();
}
@Override
public int computeTotalExecutors() {
return Label.this.getTotalExecutors();
}
@Override
public int computeQueueLength() {
return Jenkins.get().getQueue().countBuildableItemsFor(Label.this);
}
@Override
protected Set<Node> getNodes() {
return Label.this.getNodes();

View File

@ -36,8 +36,6 @@ import java.awt.BasicStroke;
import java.awt.Color;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.concurrent.TimeUnit;
import jenkins.model.Jenkins;
@ -74,12 +72,6 @@ import org.kohsuke.stapler.export.ExportedBean;
*/
@ExportedBean
public abstract class LoadStatistics {
/**
* {@code true} if and only if the new way of building statistics has been implemented by this class.
* @since 1.607
*/
private final boolean modern;
/**
* Number of executors defined for Jenkins and how it changes over time.
* @since 1.607
@ -154,35 +146,6 @@ public abstract class LoadStatistics {
this.queueLength = new MultiStageTimeSeries(
Messages._LoadStatistics_Legends_QueueLength(), ColorPalette.GREY, 0, DECAY);
this.totalExecutors = onlineExecutors;
modern = isModern(getClass());
}
/*package*/ static boolean isModern(Class<? extends LoadStatistics> clazz) {
// cannot use Util.isOverridden as these are protected methods.
boolean hasGetNodes = false;
boolean hasMatches = false;
while (clazz != LoadStatistics.class && clazz != null && !(hasGetNodes && hasMatches)) {
if (!hasGetNodes) {
try {
final Method getNodes = clazz.getDeclaredMethod("getNodes");
hasGetNodes = !Modifier.isAbstract(getNodes.getModifiers());
} catch (NoSuchMethodException e) {
// ignore
}
}
if (!hasMatches) {
try {
final Method getNodes = clazz.getDeclaredMethod("matches", Queue.Item.class, SubTask.class);
hasMatches = !Modifier.isAbstract(getNodes.getModifiers());
} catch (NoSuchMethodException e) {
// ignore
}
}
if (!(hasGetNodes && hasMatches) && LoadStatistics.class.isAssignableFrom(clazz.getSuperclass())) {
clazz = (Class<? extends LoadStatistics>) clazz.getSuperclass();
}
}
return hasGetNodes && hasMatches;
}
/**
@ -198,21 +161,27 @@ public abstract class LoadStatistics {
* @deprecated use {@link #computeSnapshot()} and then {@link LoadStatisticsSnapshot#getIdleExecutors()}
*/
@Deprecated
public abstract int computeIdleExecutors();
public int computeIdleExecutors() {
return computeSnapshot().getIdleExecutors();
}
/**
* Computes the # of total executors right now and obtains the snapshot value.
* @deprecated use {@link #computeSnapshot()} and then {@link LoadStatisticsSnapshot#getOnlineExecutors()}
*/
@Deprecated
public abstract int computeTotalExecutors();
public int computeTotalExecutors() {
return computeSnapshot().getOnlineExecutors();
}
/**
* Computes the # of queue length right now and obtains the snapshot value.
* @deprecated use {@link #computeSnapshot()} and then {@link LoadStatisticsSnapshot#getQueueLength()}
*/
@Deprecated
public abstract int computeQueueLength();
public int computeQueueLength() {
return computeSnapshot().getQueueLength();
}
/**
* Creates a trend chart.
@ -333,13 +302,7 @@ public abstract class LoadStatistics {
* @since 1.607
*/
public LoadStatisticsSnapshot computeSnapshot() {
if (modern) {
return computeSnapshot(Jenkins.get().getQueue().getBuildableItems());
} else {
int t = computeTotalExecutors();
int i = computeIdleExecutors();
return new LoadStatisticsSnapshot(t, t, Math.max(i - t, 0), Math.max(t - i, 0), i, i, computeQueueLength());
}
}
/**

View File

@ -56,21 +56,6 @@ public class OverallLoadStatistics extends LoadStatistics {
super(0, 0);
}
@Override
public int computeIdleExecutors() {
return new ComputerSet().getIdleExecutors();
}
@Override
public int computeTotalExecutors() {
return new ComputerSet().getTotalExecutors();
}
@Override
public int computeQueueLength() {
return Jenkins.get().getQueue().countBuildableItems();
}
@Override
protected Iterable<Node> getNodes() {
return Jenkins.get().getNodes();

View File

@ -24,7 +24,6 @@
package jenkins.model;
import hudson.model.Computer;
import hudson.model.LoadStatistics;
import hudson.model.Node;
import hudson.model.Node.Mode;
@ -52,35 +51,6 @@ public class UnlabeledLoadStatistics extends LoadStatistics {
super(0, 0);
}
@Override
public int computeIdleExecutors() {
int r = 0;
for (Computer c : Jenkins.get().getComputers()) {
Node node = c.getNode();
if (node != null && node.getMode() == Mode.NORMAL && (c.isOnline() || c.isConnecting()) && c.isAcceptingTasks()) {
r += c.countIdle();
}
}
return r;
}
@Override
public int computeTotalExecutors() {
int r = 0;
for (Computer c : Jenkins.get().getComputers()) {
Node node = c.getNode();
if (node != null && node.getMode() == Mode.NORMAL && c.isOnline()) {
r += c.countExecutors();
}
}
return r;
}
@Override
public int computeQueueLength() {
return Jenkins.get().getQueue().strictCountBuildableItemsFor(null);
}
@Override
protected Iterable<Node> getNodes() {
return nodes;

View File

@ -24,8 +24,6 @@
package hudson.model;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.Assume.assumeFalse;
import hudson.Functions;
@ -50,21 +48,6 @@ public class LoadStatisticsTest {
public void graph() throws IOException {
assumeFalse("TODO: Implement this test on Windows", Functions.isWindows());
LoadStatistics ls = new LoadStatistics(0, 0) {
@Override
public int computeIdleExecutors() {
throw new UnsupportedOperationException();
}
@Override
public int computeTotalExecutors() {
throw new UnsupportedOperationException();
}
@Override
public int computeQueueLength() {
throw new UnsupportedOperationException();
}
@Override
protected Iterable<Node> getNodes() {
throw new UnsupportedOperationException();
@ -100,42 +83,4 @@ public class LoadStatisticsTest {
tempFile.delete();
}
}
@Test
public void isModernWorks() {
assertThat(LoadStatistics.isModern(Modern.class), is(true));
assertThat(LoadStatistics.isModern(LoadStatistics.class), is(false));
}
private static class Modern extends LoadStatistics {
protected Modern(int initialOnlineExecutors, int initialBusyExecutors) {
super(initialOnlineExecutors, initialBusyExecutors);
}
@Override
public int computeIdleExecutors() {
return 0;
}
@Override
public int computeTotalExecutors() {
return 0;
}
@Override
public int computeQueueLength() {
return 0;
}
@Override
protected Iterable<Node> getNodes() {
return null;
}
@Override
protected boolean matches(Queue.Item item, SubTask subTask) {
return false;
}
}
}

View File

@ -61,7 +61,7 @@ public class UnlabeledLoadStatisticsTest {
public void computeQueueLength() throws Exception {
final Queue queue = j.jenkins.getQueue();
assertEquals("Queue must be empty when the test starts", 0, queue.getBuildableItems().size());
assertEquals("Statistics must return 0 when the test starts", 0, unlabeledLoad.computeQueueLength());
assertEquals("Statistics must return 0 when the test starts", 0, unlabeledLoad.computeSnapshot().getQueueLength());
// Disable builds by default, create an agent to prevent assigning of "built-in" labels
j.jenkins.setNumExecutors(0);
@ -77,22 +77,22 @@ public class UnlabeledLoadStatisticsTest {
// Put unlabeled build into the queue
unlabeledProject.scheduleBuild2(0, new ParametersAction(new StringParameterValue("FOO", "BAR1")));
queue.maintain();
assertEquals("Unlabeled build must be taken into account", 1, unlabeledLoad.computeQueueLength());
assertEquals("Unlabeled build must be taken into account", 1, unlabeledLoad.computeSnapshot().getQueueLength());
unlabeledProject.scheduleBuild2(0, new ParametersAction(new StringParameterValue("FOO", "BAR2")));
queue.maintain();
assertEquals("Second Unlabeled build must be taken into account", 2, unlabeledLoad.computeQueueLength());
assertEquals("Second Unlabeled build must be taken into account", 2, unlabeledLoad.computeSnapshot().getQueueLength());
// Put labeled build into the queue
labeledProject.scheduleBuild2(0);
queue.maintain();
assertEquals("Labeled builds must be ignored", 2, unlabeledLoad.computeQueueLength());
assertEquals("Labeled builds must be ignored", 2, unlabeledLoad.computeSnapshot().getQueueLength());
// Allow executions of unlabeled builds on built-in node, all unlabeled builds should pass
j.jenkins.setNumExecutors(1);
j.buildAndAssertSuccess(unlabeledProject);
queue.maintain();
assertEquals("Queue must contain the labeled project build", 1, queue.getBuildableItems().size());
assertEquals("Statistics must return 0 after all builds", 0, unlabeledLoad.computeQueueLength());
assertEquals("Statistics must return 0 after all builds", 0, unlabeledLoad.computeSnapshot().getQueueLength());
}
}