Issue
A Checkpoint restart on a Pipeline stage causes a Deserializatoin/FlowhHead Error with output such as below:
hudson.remoting.ProxyException: an exception which occurred:
in object of type org.jenkinsci.plugins.workflow.cps.FlowHead
in field org.jenkinsci.plugins.workflow.cps.CpsThread.head
in object org.jenkinsci.plugins.workflow.cps.CpsThread@69f1cf1d
...
Caused: hudson.remoting.ProxyException: java.lang.IllegalStateException: FlowHead loading problem at deserialize: Null FlowHead with id 22 in execution CpsFlowExecution
at org.jenkinsci.plugins.workflow.cps.FlowHead.readResolve(FlowHead.java:196)
Environment
- CloudBees CI (CloudBees Core)
- CloudBees CI (CloudBees Core) on modern cloud platforms - Managed Master
- CloudBees CI (CloudBees Core) on modern cloud platforms - Operations Center
- CloudBees CI (CloudBees Core) on traditional platforms - Client Master
- CloudBees CI (CloudBees Core) on traditional platforms - Operations Center
- CloudBees Jenkins Enterprise - Managed Master
- CloudBees Jenkins Enterprise - Operations Center
- CloudBees Jenkins Platform - Client Master
- CloudBees Jenkins Platform - Operations Center
Description
This deserialization issue on checkpoints is possible when using the Pipeline Groovy/workflow-cps
Plugin on Version 2.50+ and using nested parallel
blocks before a checkpoint
call.
Resolution
The fix involves switching from a nested parallel format (calling a parallel within a parallel) and instead moving to combining these parallels into a single parallel call.
For example, the code below shows a nested parallel which would trigger this deserialization issue if a checkpoint was used directly after:
def labels = ['node1', 'node2'] // labels for Jenkins node types we will build on
def environments = ['dev', 'prod'];
def outerParallel = [:]
// The following creates the Nested Parallels:
for (x in labels) {
def label = x // Need to bind the label variable before the closure - can't do 'for (label in labels)'
// Create a map to pass in to the 'parallel' step so we can fire all the builds at once
outerParallel[label] = {
def nestedParallel = [:]
for (y in environments) {
def env = y
node(label) {
nestedParallel[env] = {
echo "on label $label doing work in environment $env"
}
}
}
parallel nestedParallel
}
}
parallel outerParallel
Below is the recommended method for converting this nested parallel to an unnested parallel using a single parallel call.
def singleBigParallel = [:]
for (x in labels) {
for (y in environments) {
// Closure binding
def label = x
def env = y
node(label) {
singleBigParallel["$label-$env"] = { // Creates a branch for each combination of label and environment
echo "on label $label doing work in environment $env"
}
}
}
}
parallel singleBigParallel
0 Comments