提交 839ba1d7 编写于 作者: K kohsuke

got rid of the original method, based on the assumption that no one is...

got rid of the original method, based on the assumption that no one is implementing a custom LoadBalancer.

I thought about it, but it's too tricky to maintain compatibility, hence this choice.

git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@34626 71c3de6d-444a-0410-be80-ed276b4c234a
上级 2c449c99
......@@ -23,9 +23,6 @@
*/
package hudson.model;
import hudson.model.Node.Mode;
import hudson.model.Queue.ApplicableJobOfferList;
import hudson.model.Queue.JobOffer;
import hudson.model.Queue.Task;
import hudson.model.queue.MappingWorksheet;
import hudson.model.queue.MappingWorksheet.ExecutorChunk;
......@@ -45,7 +42,7 @@ import java.util.logging.Logger;
*/
public abstract class LoadBalancer /*implements ExtensionPoint*/ {
/**
* Chooses the executor to carry out the build for the given project.
* Chooses the executor(s) to carry out the build for the given task.
*
* <p>
* This method is invoked from different threads, but the execution is serialized by the caller.
......@@ -53,112 +50,24 @@ public abstract class LoadBalancer /*implements ExtensionPoint*/ {
* can be safely introspected from this method, if that information is necessary to make
* decisions.
*
* @param applicable
* The list of {@link JobOffer}s that represent {@linkplain JobOffer#isAvailable() available} {@link Executor}s, from which
* the callee can choose. Never null.
* @param task
* The task whose execution is being considered. Never null.
* @param worksheet
* The work sheet that represents the matching that needs to be made.
* The job of this method is to determine which work units on this worksheet
* are executed on which executors (also on this worksheet.)
*
* @return
* Pick one of the items from {@code available}, and return it. How you choose it
* is the crucial part of the implementation. Return null if you don't want
* the task to be executed right now, in which case this method will be called
* some time later with the same task.
* Build up the mapping by using the given worksheet and return it.
* Return null if you don't want the task to be executed right now,
* in which case this method will be called some time later with the same task.
*/
protected abstract JobOffer choose(Task task, ApplicableJobOfferList applicable);
protected abstract Mapping map(Task task, MappingWorksheet worksheet);
/**
* Traditional implementation of this.
*/
public static final LoadBalancer DEFAULT = new LoadBalancer() {
protected JobOffer choose(Task task, ApplicableJobOfferList applicable) {
Label l = task.getAssignedLabel();
if (l != null) {
// if a project has assigned label, it can be only built on it
for (JobOffer offer : applicable) {
if (l.contains(offer.getNode()))
return offer;
}
return null;
}
// if we are a large deployment, then we will favor slaves
boolean isLargeHudson = Hudson.getInstance().getNodes().size() > 10;
// otherwise let's see if the last node where this project was built is available
// it has up-to-date workspace, so that's usually preferable.
// (but we can't use an exclusive node)
Node n = task.getLastBuiltOn();
if (n != null && n.getMode() == Mode.NORMAL) {
for (JobOffer offer : applicable) {
if (offer.getNode() == n) {
if (isLargeHudson && offer.getNode() instanceof Hudson)
// but if we are a large Hudson, then we really do want to keep the master free from builds
continue;
return offer;
}
}
}
// duration of a build on a slave tends not to have an impact on
// the master/slave communication, so that means we should favor
// running long jobs on slaves.
// Similarly if we have many slaves, master should be made available
// for HTTP requests and coordination as much as possible
if (isLargeHudson || task.getEstimatedDuration() > 15 * 60 * 1000) {
// consider a long job to be > 15 mins
for (JobOffer offer : applicable) {
if (offer.getNode() instanceof Slave && offer.isNotExclusive())
return offer;
}
}
// lastly, just look for any idle executor
for (JobOffer offer : applicable) {
if (offer.isNotExclusive())
return offer;
}
// nothing available
return null;
}
@Override
protected Mapping map(Task task, MappingWorksheet worksheet) {
// TODO:
return CONSISTENT_HASH.map(task,worksheet);
}
};
/**
* Uses a consistent hash for scheduling.
*/
public static final LoadBalancer CONSISTENT_HASH = new LoadBalancer() {
protected JobOffer choose(Task task, ApplicableJobOfferList applicable) {
// populate a consistent hash linear to the # of executors
// TODO: there's a lot of room for innovations here
// TODO: do this upfront and reuse the consistent hash
ConsistentHash<Node> hash = new ConsistentHash<Node>(new Hash<Node>() {
public String hash(Node node) {
return node.getNodeName();
}
});
for (Node n : applicable.nodes())
hash.add(n,n.getNumExecutors()*100);
// TODO: add some salt as a query point so that the user can tell Hudson to hop the project to a new node
for(Node n : hash.list(task.getFullDisplayName())) {
JobOffer o = applicable._for(n);
if(o!=null)
return o;
}
// nothing available
return null;
}
@Override
protected Mapping map(Task task, MappingWorksheet ws) {
// build consistent hash for each work chunk
......@@ -207,6 +116,15 @@ public abstract class LoadBalancer /*implements ExtensionPoint*/ {
}
};
/**
* Traditional implementation of this.
*
* @deprecated as of 1.FATTASK
* The only implementation in the core now is the one based on consistent hash.
*/
public static final LoadBalancer DEFAULT = CONSISTENT_HASH;
/**
* Wraps this {@link LoadBalancer} into a decorator that tests the basic sanity of the implementation.
* Only override this if you find some of the checks excessive, but beware that it's like driving without a seat belt.
......@@ -214,17 +132,6 @@ public abstract class LoadBalancer /*implements ExtensionPoint*/ {
protected LoadBalancer sanitize() {
final LoadBalancer base = this;
return new LoadBalancer() {
@Override
protected JobOffer choose(Task task, ApplicableJobOfferList applicable) {
if (Queue.ifBlockedByHudsonShutdown(task)) {
// if we are quieting down, don't start anything new so that
// all executors will be eventually free.
return null;
}
return base.choose(task, applicable);
}
@Override
protected Mapping map(Task task, MappingWorksheet worksheet) {
if (Queue.ifBlockedByHudsonShutdown(task)) {
......
......@@ -23,6 +23,7 @@
*/
package hudson.model;
import hudson.AbortException;
import hudson.BulkChange;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
......@@ -800,7 +801,7 @@ public class Queue extends ResourceController implements Saveable {
JobOffer offer = parked.remove(exec);
if (offer != null && offer.workUnit != null) {
// we are already assigned a project, but now we can't handle it.
offer.workUnit.context.abort();
offer.workUnit.context.abort(new AbortException());
}
// since this executor might have been chosen for
......@@ -811,63 +812,6 @@ public class Queue extends ResourceController implements Saveable {
}
}
/**
* Represents a list of {@linkplain JobOffer#canTake(Task) applicable} {@link JobOffer}s
* and provides various typical
*/
public final class ApplicableJobOfferList implements Iterable<JobOffer> {
private final List<JobOffer> list;
// laziy filled
private Map<Node,List<JobOffer>> nodes;
private ApplicableJobOfferList(Task task) {
list = new ArrayList<JobOffer>(parked.size());
for (JobOffer j : parked.values())
if(j.canTake(task))
list.add(j);
}
/**
* Returns all the {@linkplain JobOffer#isAvailable() available} {@link JobOffer}s.
*/
public List<JobOffer> all() {
return list;
}
public Iterator<JobOffer> iterator() {
return list.iterator();
}
/**
* List up all the {@link Node}s that have some available offers.
*/
public Set<Node> nodes() {
return byNodes().keySet();
}
/**
* Gets a {@link JobOffer} for an executor of the given node, if any.
* Otherwise null.
*/
public JobOffer _for(Node n) {
List<JobOffer> r = byNodes().get(n);
if(r==null) return null;
return r.get(0);
}
public Map<Node,List<JobOffer>> byNodes() {
if(nodes==null) {
nodes = new HashMap<Node,List<JobOffer>>();
for (JobOffer o : list) {
List<JobOffer> l = nodes.get(o.getNode());
if(l==null) nodes.put(o.getNode(),l=new ArrayList<JobOffer>());
l.add(o);
}
}
return nodes;
}
}
/**
* Checks the queue and runs anything that can be run.
*
......
TODO:
- If a subtask is interrupted, all the executors should be aborted.
x If a subtask is interrupted, all the executors should be aborted.
- handle AbstractMethodError on methods newly added.
- LoadBalancer backward compatibility story.
- Document the new abstraction
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册