PageRenderTime 25ms CodeModel.GetById 2ms app.highlight 17ms RepoModel.GetById 1ms app.codeStats 1ms

/hudson-core/src/main/java/hudson/model/LoadBalancer.java

http://github.com/hudson/hudson
Java | 157 lines | 65 code | 19 blank | 73 comment | 8 complexity | 7ee843ae10d0e61bbfb9e3083d343e50 MD5 | raw file
  1/*
  2 * The MIT License
  3 *
  4 * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi
  5 *
  6 * Permission is hereby granted, free of charge, to any person obtaining a copy
  7 * of this software and associated documentation files (the "Software"), to deal
  8 * in the Software without restriction, including without limitation the rights
  9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10 * copies of the Software, and to permit persons to whom the Software is
 11 * furnished to do so, subject to the following conditions:
 12 *
 13 * The above copyright notice and this permission notice shall be included in
 14 * all copies or substantial portions of the Software.
 15 *
 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 22 * THE SOFTWARE.
 23 */
 24package hudson.model;
 25
 26import hudson.model.Queue.Task;
 27import hudson.model.queue.MappingWorksheet;
 28import hudson.model.queue.MappingWorksheet.ExecutorChunk;
 29import hudson.model.queue.MappingWorksheet.Mapping;
 30import hudson.util.ConsistentHash;
 31import hudson.util.ConsistentHash.Hash;
 32
 33import java.util.ArrayList;
 34import java.util.List;
 35import java.util.logging.Logger;
 36
 37/**
 38 * Strategy that decides which {@link Task} gets run on which {@link Executor}.
 39 *
 40 * @author Kohsuke Kawaguchi
 41 * @since 1.301
 42 */
 43public abstract class LoadBalancer /*implements ExtensionPoint*/ {
 44    /**
 45     * Chooses the executor(s) to carry out the build for the given task.
 46     *
 47     * <p>
 48     * This method is invoked from different threads, but the execution is serialized by the caller.
 49     * The thread that invokes this method always holds a lock to {@link Queue}, so queue contents
 50     * can be safely introspected from this method, if that information is necessary to make
 51     * decisions.
 52     * 
 53     * @param  task
 54     *      The task whose execution is being considered. Never null.
 55     * @param worksheet
 56     *      The work sheet that represents the matching that needs to be made.
 57     *      The job of this method is to determine which work units on this worksheet
 58     *      are executed on which executors (also on this worksheet.)
 59     *
 60     * @return
 61     *      Build up the mapping by using the given worksheet and return it.
 62     *      Return null if you don't want the task to be executed right now,
 63     *      in which case this method will be called some time later with the same task.
 64     */
 65    public abstract Mapping map(Task task, MappingWorksheet worksheet);
 66
 67    /**
 68     * Uses a consistent hash for scheduling.
 69     */
 70    public static final LoadBalancer CONSISTENT_HASH = new LoadBalancer() {
 71        @Override
 72        public Mapping map(Task task, MappingWorksheet ws) {
 73            // build consistent hash for each work chunk
 74            List<ConsistentHash<ExecutorChunk>> hashes = new ArrayList<ConsistentHash<ExecutorChunk>>(ws.works.size());
 75            for (int i=0; i<ws.works.size(); i++) {
 76                ConsistentHash<ExecutorChunk> hash = new ConsistentHash<ExecutorChunk>(new Hash<ExecutorChunk>() {
 77                    public String hash(ExecutorChunk node) {
 78                        return node.getName();
 79                    }
 80                });
 81                for (ExecutorChunk ec : ws.works(i).applicableExecutorChunks())
 82                    hash.add(ec,ec.size()*100);
 83
 84                hashes.add(hash);
 85            }
 86
 87            // do a greedy assignment
 88            Mapping m = ws.new Mapping();
 89            assert m.size()==ws.works.size();   // just so that you the reader of the source code don't get confused with the for loop index
 90
 91            if (assignGreedily(m,task,hashes,0)) {
 92                assert m.isCompletelyValid();
 93                return m;
 94            } else
 95                return null;
 96        }
 97
 98        private boolean assignGreedily(Mapping m, Task task, List<ConsistentHash<ExecutorChunk>> hashes, int i) {
 99            if (i==hashes.size())   return true;    // fully assigned
100
101            String key = task.getFullDisplayName() + (i>0 ? String.valueOf(i) : "");
102
103            for (ExecutorChunk ec : hashes.get(i).list(key)) {
104                // let's attempt this assignment
105                m.assign(i,ec);
106
107                if (m.isPartiallyValid() && assignGreedily(m,task,hashes,i+1))
108                    return true;    // successful greedily allocation
109
110                // otherwise 'ec' wasn't a good fit for us. try next.
111            }
112
113            // every attempt failed
114            m.assign(i,null);
115            return false;
116        }
117    };
118
119    /**
120     * Traditional implementation of this.
121     *
122     * @deprecated as of 1.377
123     *      The only implementation in the core now is the one based on consistent hash.
124     */
125    public static final LoadBalancer DEFAULT = CONSISTENT_HASH;
126
127
128    /**
129     * Wraps this {@link LoadBalancer} into a decorator that tests the basic sanity of the implementation.
130     * Only override this if you find some of the checks excessive, but beware that it's like driving without a seat belt.
131     */
132    protected LoadBalancer sanitize() {
133        final LoadBalancer base = this;
134        return new LoadBalancer() {
135            @Override
136            public Mapping map(Task task, MappingWorksheet worksheet) {
137                if (Queue.ifBlockedByHudsonShutdown(task)) {
138                    // if we are quieting down, don't start anything new so that
139                    // all executors will be eventually free.
140                    return null;
141                }
142                return base.map(task, worksheet);
143            }
144
145            /**
146             * Double-sanitization is pointless.
147             */
148            @Override
149            protected LoadBalancer sanitize() {
150                return this;
151            }
152
153            private final Logger LOGGER = Logger.getLogger(LoadBalancer.class.getName());
154        };
155    }
156
157}