Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions src/doc/examples/avoiding-executor-starvation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# Avoiding Executor Starvation

When a pipeline uses `agent any` (or a specific label) at the top level together
with `options { lock(...) }`, every build allocates an executor **before**
trying to acquire the lock. If the resource is busy the build sits on the
executor doing nothing — preventing other jobs from using it. This is called
**executor starvation**.

## The problem

```groovy
pipeline {
agent {
label 'some-label'
}
options {
lock('end-to-end-test-resource')
}
stages {
stage('Test') {
steps {
echo 'Running end-to-end tests...'
}
}
}
}
```

With this layout, if three builds of the same job are queued and there is only
one `end-to-end-test-resource`, all three grab an executor yet only one can
proceed. The other two hold their executors hostage while they wait for the
lock, blocking any other job that needs to run on those executors.

## The solution — `agent none` + stage-level agent

Move the `agent` directive **inside the stage** that needs the lock:

```groovy
pipeline {
agent none // no executor allocated up front
stages {
stage('Test') {
options {
lock('end-to-end-test-resource') // acquire lock first …
}
agent {
label 'some-label' // … then allocate an executor
}
steps {
echo 'Running end-to-end tests...'
}
}
}
}
```

Now a build waiting for the lock does **not** occupy an executor. Once the lock
is acquired the stage allocates the agent and runs. Other jobs can use the
executors in the meantime.

## Preparation stages that don't need the lock

If your build has work that can run without the resource, split it into a
separate stage so the lock is held only when necessary:

```groovy
pipeline {
agent none
stages {
stage('Build') {
agent any
steps {
echo 'Compiling — no lock needed'
}
}
stage('Deploy') {
options {
lock resource: 'deploy-target'
}
agent any
steps {
echo 'Deploying — lock held'
}
}
}
}
```

## Multiple stages under one lock

If several stages need the same resource, wrap them in a parent stage:

```groovy
pipeline {
agent none
stages {
stage('Deploy and Test') {
options {
lock resource: 'test-environment'
}
agent any
stages {
stage('Deploy') {
steps {
echo 'Deploying...'
}
}
stage('Integration Test') {
steps {
echo 'Testing...'
}
}
}
}
}
}
```

The lock is acquired once for the parent stage and released after the last
nested stage completes. No executor is consumed while waiting.

## Scripted pipeline equivalent

In scripted pipelines the same principle applies — acquire the lock **before**
allocating a node:

```groovy
lock('end-to-end-test-resource') {
node('some-label') {
echo 'Running with lock and node'
}
}
```

See also [Node dependent resources](lock-nodes.md) for more scripted examples.

## See also

- [Lock specific stages](lock-specific-stages.md)
- [Locking multiple stages in declarative pipeline](locking-multiple-stages-in-declarative-pipeline.md)
- [Node dependent resources](lock-nodes.md)
1 change: 1 addition & 0 deletions src/doc/examples/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Examples of lockable resources include:
If you have an example to share, please create a [new documentation issue](https://github.com/jenkinsci/lockable-resources-plugin/issues/new?assignees=&labels=documentation&template=3-documentation.yml) and provide additional examples as a [pull request](https://github.com/jenkinsci/lockable-resources-plugin/pulls) to the repository.
If you have a question, please open a [GitHub issue](https://github.com/jenkinsci/lockable-resources-plugin/issues/new/choose) with your question.

- [Avoiding executor starvation](avoiding-executor-starvation.md)
- [Node depended resources](lock-nodes.md)
- [Lock specific stages](lock-specific-stages.md)
- [Locking multiple stages in declarative pipeline](locking-multiple-stages-in-declarative-pipeline.md)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.jenkins.plugins.lockableresources;
package org.jenkins.plugins.lockableresources.nodes;

import hudson.Extension;
import hudson.init.InitMilestone;
Expand All @@ -10,6 +10,8 @@
import java.util.stream.Collectors;
import jenkins.model.Jenkins;
import jenkins.util.SystemProperties;
import org.jenkins.plugins.lockableresources.LockableResource;
import org.jenkins.plugins.lockableresources.LockableResourcesManager;
import org.jenkins.plugins.lockableresources.util.Constants;

// -----------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public void run() {
public void run() {
System.setProperty(Constants.SYSTEM_PROPERTY_ENABLE_NODE_MIRROR, "true");
LOGGER.info("run NodesMirror");
org.jenkins.plugins.lockableresources.NodesMirror.createNodeResources();
org.jenkins.plugins.lockableresources.nodes.NodesMirror.createNodeResources();
LOGGER.info("NodesMirror done");
}
};
Expand Down
Loading
Loading