提交 eafdaa44 编写于 作者: K Kohsuke Kawaguchi

QueueTaskDispatcher should participate in classifying Queue#blockedProjects and Queue#buildables.

This helps Cloud and NodeProvisioner do its work correctly, and for many implementations this is more efficient.
上级 c05522c2
......@@ -141,8 +141,9 @@ public class Queue extends ResourceController implements Saveable {
/**
* {@link Task}s that can be built immediately
* but blocked because another build is in progress,
* required {@link Resource}s are not available, or otherwise blocked
* by {@link Task#isBuildBlocked()}.
* required {@link Resource}s are not available,
* blocked via {@link QueueTaskDispatcher#canRun(Item)},
* or otherwise blocked by {@link Task#isBuildBlocked()}.
*/
private final ItemList<BlockedItem> blockedProjects = new ItemList<BlockedItem>();
......@@ -787,7 +788,7 @@ public class Queue extends ResourceController implements Saveable {
BuildableItem p = itr.next();
// one last check to make sure this build is not blocked.
if (isBuildBlocked(p.task)) {
if (isBuildBlocked(p)) {
itr.remove();
blockedProjects.put(p.task,new BlockedItem(p));
continue;
......@@ -885,10 +886,19 @@ public class Queue extends ResourceController implements Saveable {
}
/**
* Checks if the given task is blocked.
* Checks if the given item should be prevented from entering into the {@link #buildables} state
* and instead stay in the {@link #blockedProjects} state.
*/
private boolean isBuildBlocked(Task t) {
return t.isBuildBlocked() || !canRun(t.getResourceList());
private boolean isBuildBlocked(Item i) {
if (i.task.isBuildBlocked() || !canRun(i.task.getResourceList()) || !allowNewBuildableTask(i.task))
return true;
for (QueueTaskDispatcher d : QueueTaskDispatcher.all()) {
if (d.canRun(i)!=null)
return true;
}
return false;
}
/**
......@@ -907,9 +917,15 @@ public class Queue extends ResourceController implements Saveable {
/**
* Queue maintenance.
*
* <p>
* Move projects between {@link #waitingList}, {@link #blockedProjects}, and {@link #buildables}
* appropriately.
*
* <p>
* Jenkins internally invokes this method by itself whenever there's a change that can affect
* the scheduling (such as new node becoming online, # of executors change, a task completes execution, etc.),
* and it also gets invoked periodically (see {@link MaintainTask}.)
*/
public synchronized void maintain() {
if (LOGGER.isLoggable(Level.FINE))
......@@ -919,7 +935,7 @@ public class Queue extends ResourceController implements Saveable {
Iterator<BlockedItem> itr = blockedProjects.values().iterator();
while (itr.hasNext()) {
BlockedItem p = itr.next();
if (!isBuildBlocked(p.task) && allowNewBuildableTask(p.task)) {
if (!isBuildBlocked(p)) {
// ready to be executed
LOGGER.fine(p.task.getFullDisplayName() + " no longer blocked");
itr.remove();
......@@ -935,7 +951,7 @@ public class Queue extends ResourceController implements Saveable {
waitingList.remove(top);
Task p = top.task;
if (!isBuildBlocked(p) && allowNewBuildableTask(p)) {
if (!isBuildBlocked(top)) {
// ready to be executed immediately
LOGGER.fine(p.getFullDisplayName() + " ready to build");
makeBuildable(new BuildableItem(top));
......@@ -1458,12 +1474,13 @@ public class Queue extends ResourceController implements Saveable {
}
public CauseOfBlockage getCauseOfBlockage() {
Jenkins hudson = Jenkins.getInstance();
Jenkins jenkins = Jenkins.getInstance();
if(ifBlockedByHudsonShutdown(task))
return CauseOfBlockage.fromMessage(Messages._Queue_HudsonIsAboutToShutDown());
Label label = getAssignedLabel();
if (hudson.getNodes().isEmpty())
List<Node> allNodes = jenkins.getNodes();
if (allNodes.isEmpty())
label = null; // no master/slave. pointless to talk about nodes
if (label != null) {
......@@ -1476,10 +1493,26 @@ public class Queue extends ResourceController implements Saveable {
else return new BecauseNodeIsBusy(nodes.iterator().next());
}
} else {
CauseOfBlockage c = null;
for (Node node : allNodes) {
if (node.toComputer().isPartiallyIdle()) {
c = canTake(node);
if (c==null) break;
}
}
return CauseOfBlockage.createNeedsMoreExecutor(Messages._Queue_WaitingForNextAvailableExecutor());
}
}
private CauseOfBlockage canTake(Node node) {
for (QueueTaskDispatcher d : QueueTaskDispatcher.all()) {
CauseOfBlockage cause = d.canTake(node, this);
if (cause!=null) return cause;
}
return null;
}
@Override
public boolean isStuck() {
Label label = getAssignedLabel();
......
......@@ -27,6 +27,8 @@ package hudson.model.queue;
import hudson.Extension;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.model.Queue.Item;
import hudson.slaves.Cloud;
import jenkins.model.Jenkins;
import hudson.model.Node;
import hudson.model.Queue;
......@@ -69,7 +71,7 @@ public abstract class QueueTaskDispatcher implements ExtensionPoint {
}
/**
* Called whenever {@link Queue} is considering to execute the given task on a given node.
* Called when {@link Queue} is deciding where to execute the given task.
*
* <p>
* Implementations can return null to indicate that the assignment is fine, or it can return
......@@ -82,6 +84,13 @@ public abstract class QueueTaskDispatcher implements ExtensionPoint {
* the decision needs to be made quickly.
*
* <p>
* This method is primarily designed to fine-tune where the execution should take place. If the execution
* shouldn't commence anywhere at all, implementation should use {@link #canRun(Item)} instead so
* that Jenkins understands the difference between "this node isn't the right place for this work"
* vs "the time isn't right for this work to execute." This affects the provisioning behaviour
* with elastic Jenkins deployments.
*
* <p>
* Vetos are additive. When multiple {@link QueueTaskDispatcher}s are in the system,
* the task won't run on the given node if any one of them returns a non-null value.
* (This relationship is also the same with built-in check logic.)
......@@ -92,6 +101,38 @@ public abstract class QueueTaskDispatcher implements ExtensionPoint {
return canTake(node,item.task); // backward compatible behaviour
}
/**
* Called whenever {@link Queue} is considering if {@link Queue.Item} is ready to execute immediately
* (which doesn't necessarily mean that it gets executed right away &mdash; it's still subject to
* executor availability), or if it should be considered blocked.
*
* <p>
* Compared to {@link #canTake(Node, BuildableItem)}, this version tells Jenkins that the task is
* simply not ready to execute, even if there's available executor. This is more efficient
* than {@link #canTake(Node, BuildableItem)}, and it sends the right signal to Jenkins so that
* it won't use {@link Cloud} to try to provision new executors.
*
* <p>
* Vetos are additive. When multiple {@link QueueTaskDispatcher}s are in the system,
* the task is considered blocked if any one of them returns a non-null value.
* (This relationship is also the same with built-in check logic.)
*
* <p>
* If a {@link QueueTaskDispatcher} returns non-null from this method, the task is placed into
* the 'blocked' state, and generally speaking it stays in this state for a few seconds before
* its state gets re-evaluated. If a {@link QueueTaskDispatcher} wants the blockage condition
* to be re-evaluated earlier, call {@link Queue#scheduleMaintenance()} to initiate that process.
*
* @return
* null to indicate that the item is ready to proceed to the buildable state as far as this
* {@link QueueTaskDispatcher} is concerned. Otherwise return an object that indicates why
* the build is blocked.
* @since 1.427
*/
public CauseOfBlockage canRun(Queue.Item item) {
return null;
}
/**
* All registered {@link QueueTaskDispatcher}s.
*/
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册