Skip to content

Commit

Permalink
Merge pull request #129 from kuleuven/feat/fast-provisioning
Browse files Browse the repository at this point in the history
feat: speedup provisioning of new nodes
  • Loading branch information
multani authored Apr 7, 2022
2 parents 2a8f212 + 2b9049c commit 7f12542
Showing 1 changed file with 60 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@

import hudson.Extension;
import hudson.model.Label;
import hudson.model.Queue;
import hudson.model.LoadStatistics.LoadStatisticsSnapshot;
import hudson.slaves.Cloud;
import hudson.slaves.NodeProvisioner;
import hudson.slaves.NodeProvisioner.PlannedNode;
import jenkins.model.Jenkins;
import jenkins.util.Timer;
import hudson.model.queue.QueueListener;
import hudson.slaves.CloudProvisioningListener;

import edu.umd.cs.findbugs.annotations.NonNull;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

Expand Down Expand Up @@ -39,26 +44,33 @@ public NodeProvisioner.StrategyDecision apply(@NonNull NodeProvisioner.StrategyS
for (Cloud nomadCloud : Jenkins.get().clouds) {
if (nomadCloud instanceof NomadCloud) {

LOGGER.log(Level.FINE, "Available executors={0} connecting executors={1} AdditionalPlannedCapacity={2} pending ={3}",
new Object[]{snapshot.getAvailableExecutors(), snapshot.getConnectingExecutors(), strategyState.getAdditionalPlannedCapacity(), ((NomadCloud) nomadCloud).getPending()});
LOGGER.log(Level.FINE,
"Available executors={0} connecting executors={1} AdditionalPlannedCapacity={2} pending ={3}",
new Object[] { snapshot.getAvailableExecutors(), snapshot.getConnectingExecutors(),
strategyState.getAdditionalPlannedCapacity(), ((NomadCloud) nomadCloud).getPending() });
int availableCapacity = snapshot.getAvailableExecutors() +
snapshot.getConnectingExecutors() +
strategyState.getAdditionalPlannedCapacity() +
((NomadCloud) nomadCloud).getPending();

strategyState.getPlannedCapacitySnapshot();
int previousCapacity = availableCapacity;
int currentDemand = snapshot.getQueueLength();

LOGGER.log(Level.FINE, "Available capacity=" + availableCapacity + " currentDemand=" + currentDemand);

if (availableCapacity < currentDemand) {
Collection<PlannedNode> plannedNodes = nomadCloud.provision(label, currentDemand - availableCapacity);
Collection<PlannedNode> plannedNodes = nomadCloud.provision(label,
currentDemand - availableCapacity);
LOGGER.log(Level.FINE, "Planned " + plannedNodes.size() + " new nodes");

fireOnStarted(nomadCloud, strategyState.getLabel(), plannedNodes);
strategyState.recordPendingLaunches(plannedNodes);
availableCapacity += plannedNodes.size();
LOGGER.log(Level.FINE, "After provisioning, available capacity=" + availableCapacity + " currentDemand=" + currentDemand);
LOGGER.log(Level.FINE, "After provisioning, available capacity=" + availableCapacity
+ " currentDemand=" + currentDemand);
}
if (availableCapacity > previousCapacity && label != null) {
LOGGER.log(Level.FINE, "Suggesting NodeProvisioner review");
Timer.get().schedule(label.nodeProvisioner::suggestReviewNow, 1L, TimeUnit.SECONDS);
}

if (availableCapacity >= currentDemand) {
LOGGER.log(Level.FINE, "Provisioning completed");
return NodeProvisioner.StrategyDecision.PROVISIONING_COMPLETED;
Expand All @@ -71,5 +83,44 @@ public NodeProvisioner.StrategyDecision apply(@NonNull NodeProvisioner.StrategyS
LOGGER.log(Level.FINE, "Provisioning not complete, consulting remaining strategies");
return NodeProvisioner.StrategyDecision.CONSULT_REMAINING_STRATEGIES;
}
}

/**
* Force the onStarted event on the CloudProvisioningListener even if the nodes are not ready
* to notify the state as early as possible
*/
private static void fireOnStarted(final Cloud cloud, final Label label,
final Collection<NodeProvisioner.PlannedNode> plannedNodes) {
for (CloudProvisioningListener cl : CloudProvisioningListener.all()) {
try {
cl.onStarted(cloud, label, plannedNodes);
} catch (Error e) {
throw e;
} catch (Throwable e) {
LOGGER.log(Level.SEVERE, "Unexpected uncaught exception encountered while "
+ "processing onStarted() listener call in " + cl + " for label "
+ label.toString(), e);
}
}
}

/**
* Ping the nodeProvisioner as a new task enters the queue.
*/
@Extension
public static class FastProvisioning extends QueueListener {

@Override
public void onEnterBuildable(Queue.BuildableItem item) {
final Jenkins jenkins = Jenkins.get();
final Label label = item.getAssignedLabel();
for (Cloud cloud : jenkins.clouds) {
if (cloud instanceof NomadCloud && cloud.canProvision(new Cloud.CloudState(label, 0))) {
final NodeProvisioner provisioner = (label == null
? jenkins.unlabeledNodeProvisioner
: label.nodeProvisioner);
provisioner.suggestReviewNow();
}
}
}
}
}

0 comments on commit 7f12542

Please sign in to comment.