mirror of
https://github.com/jenkinsci/jenkins.git
synced 2026-11-14 04:22:16 +00:00
QueueIdStrategy (#9020)
This commit is contained in:
1 parent
229724953b
commit
c0f27461fc
5 files changed
+126
-35
No files matched your search
@@ -102,7 +102,6 @@ import java.util.TreeSet;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.logging.Level;
|
||||
@@ -113,6 +112,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.model.queue.AsynchronousExecution;
|
||||
import jenkins.model.queue.CompositeCauseOfBlockage;
|
||||
import jenkins.model.queue.QueueIdStrategy;
|
||||
import jenkins.model.queue.QueueItem;
|
||||
import jenkins.security.QueueItemAuthenticator;
|
||||
import jenkins.security.QueueItemAuthenticatorProvider;
|
||||
@@ -124,6 +124,7 @@ import jenkins.util.Timer;
|
||||
import net.jcip.annotations.GuardedBy;
|
||||
import org.jenkinsci.remoting.RoleChecker;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.Beta;
|
||||
import org.kohsuke.accmod.restrictions.DoNotUse;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
import org.kohsuke.stapler.HttpResponse;
|
||||
@@ -369,9 +370,20 @@ public class Queue extends ResourceController implements Saveable {
|
||||
/**
|
||||
* Simple queue state persistence object.
|
||||
*/
|
||||
static class State {
|
||||
public long counter;
|
||||
public List<Item> items = new ArrayList<>();
|
||||
@Restricted(Beta.class)
|
||||
public static final class State {
|
||||
public /* almost final */ List<Item> items = new ArrayList<>();
|
||||
public /* almost final */ Map<String, Object> properties = new HashMap<>();
|
||||
|
||||
private Object readResolve() {
|
||||
if (items == null) {
|
||||
items = new ArrayList<>();
|
||||
}
|
||||
if (properties == null) {
|
||||
properties = new HashMap<>();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -391,21 +403,18 @@ public class Queue extends ResourceController implements Saveable {
|
||||
Object unmarshaledObj = new XmlFile(XSTREAM, queueFile).read();
|
||||
List items;
|
||||
|
||||
State state;
|
||||
if (unmarshaledObj instanceof State) {
|
||||
State state = (State) unmarshaledObj;
|
||||
state = (State) unmarshaledObj;
|
||||
items = state.items;
|
||||
WaitingItem.COUNTER.set(state.counter);
|
||||
} else {
|
||||
// backward compatibility - it's an old List queue.xml
|
||||
items = (List) unmarshaledObj;
|
||||
long maxId = 0;
|
||||
for (Object o : items) {
|
||||
if (o instanceof Item) {
|
||||
maxId = Math.max(maxId, ((Item) o).id);
|
||||
}
|
||||
}
|
||||
WaitingItem.COUNTER.set(maxId);
|
||||
state = new State();
|
||||
state.items.addAll(items);
|
||||
}
|
||||
QueueIdStrategy.get().load(state);
|
||||
|
||||
|
||||
for (Object o : items) {
|
||||
if (o instanceof Task) {
|
||||
@@ -460,8 +469,7 @@ public class Queue extends ResourceController implements Saveable {
|
||||
try {
|
||||
// write out the queue state we want to save
|
||||
State state = new State();
|
||||
state.counter = WaitingItem.COUNTER.longValue();
|
||||
|
||||
QueueIdStrategy.get().persist(state);
|
||||
// write out the tasks on the queue
|
||||
for (Item item : getItems()) {
|
||||
if (item.task instanceof TransientTask) continue;
|
||||
@@ -2532,8 +2540,6 @@ public class Queue extends ResourceController implements Saveable {
|
||||
* {@link Item} in the {@link Queue#waitingList} stage.
|
||||
*/
|
||||
public static final class WaitingItem extends Item implements Comparable<WaitingItem> {
|
||||
private static final AtomicLong COUNTER = new AtomicLong(0);
|
||||
|
||||
/**
|
||||
* This item can be run after this time.
|
||||
*/
|
||||
@@ -2541,14 +2547,10 @@ public class Queue extends ResourceController implements Saveable {
|
||||
public Calendar timestamp;
|
||||
|
||||
public WaitingItem(Calendar timestamp, Task project, List<Action> actions) {
|
||||
super(project, actions, COUNTER.incrementAndGet(), new FutureImpl(project));
|
||||
super(project, actions, QueueIdStrategy.get().generateIdFor(project, actions), new FutureImpl(project));
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
static int getCurrentCounterValue() {
|
||||
return COUNTER.intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(WaitingItem that) {
|
||||
int r = this.timestamp.getTime().compareTo(that.timestamp.getTime());
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package jenkins.model.queue;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||
import hudson.ExtensionList;
|
||||
import hudson.ExtensionPoint;
|
||||
import hudson.model.Action;
|
||||
import hudson.model.Queue;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.Beta;
|
||||
import org.kohsuke.accmod.restrictions.DoNotUse;
|
||||
|
||||
/**
|
||||
* Pluggable strategy to generate queue item IDs as well as persist an optional opaque state whenever the queue is persisted.
|
||||
*/
|
||||
@Restricted(Beta.class)
|
||||
public abstract class QueueIdStrategy implements ExtensionPoint {
|
||||
private static final QueueIdStrategy DEFAULT = new DefaultStrategy();
|
||||
|
||||
public static QueueIdStrategy get() {
|
||||
var strategies = ExtensionList.lookup(QueueIdStrategy.class);
|
||||
if (strategies.isEmpty()) {
|
||||
return DEFAULT;
|
||||
}
|
||||
return strategies.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist the state of this strategy.
|
||||
*/
|
||||
public void persist(@NonNull Queue.State queueState) {}
|
||||
|
||||
/**
|
||||
* Loads the state of this strategy from a persisted queue state.
|
||||
*/
|
||||
public void load(@NonNull Queue.State queueState) {}
|
||||
|
||||
/**
|
||||
* Generates a new ID for the given project and actions.
|
||||
* @param project The task to be queued.
|
||||
* @param actions The actions linked the task.
|
||||
* @return a new queue ID.
|
||||
*/
|
||||
public abstract long generateIdFor(@NonNull Queue.Task project, @NonNull List<Action> actions);
|
||||
|
||||
/**
|
||||
* Default implementation if no extension is found. Simply uses a counter.
|
||||
*/
|
||||
public static final class DefaultStrategy extends QueueIdStrategy {
|
||||
private static final AtomicLong COUNTER = new AtomicLong(0);
|
||||
|
||||
@Override
|
||||
public long generateIdFor(Queue.Task project, List<Action> actions) {
|
||||
return COUNTER.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void persist(Queue.State queueState) {
|
||||
queueState.properties.put(getClass().getName(), COUNTER.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(Queue.State queueState) {
|
||||
var prop = queueState.properties.get(getClass().getName());
|
||||
if (prop instanceof Long) {
|
||||
COUNTER.set((Long) prop);
|
||||
} else {
|
||||
queueState.items.stream()
|
||||
.filter(Queue.Item.class::isInstance)
|
||||
.map(Queue.Item.class::cast)
|
||||
.max(Comparator.comparing(Queue.Item::getId))
|
||||
.ifPresentOrElse(
|
||||
maxItem -> COUNTER.set(maxItem.getId()),
|
||||
() -> COUNTER.set(0)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Restricted(DoNotUse.class) // testing only
|
||||
public static long getCurrentCounterValue() {
|
||||
return COUNTER.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user