Course outline

Jenkins / Persistent state and controlled delivery / Lesson 13

CPS execution and serializable Pipeline state

Keep orchestration state small and understand why ordinary Groovy assumptions can fail.

About 4 min readContent reviewed
On this page

Lesson goals

  • Explain the purpose and limits of Pipeline CPS transformation.
  • Use NonCPS only for pure computation that does not call Pipeline steps.

Pipeline execution can pause and persist

Jenkins Pipeline uses continuation-passing-style transformation for much of its Groovy execution so work can pause around asynchronous steps and persist enough execution state for supported resumption. That is different from running an ordinary short Groovy program to completion. Local variables and referenced objects can become part of the saved continuation. Parcel’s orchestration should retain small plain records such as candidate labels and check summaries, not large parser trees, open streams, iterators, or live Jenkins model objects. The precise persistence behavior depends on steps, plugins, and durability settings.

Serialization errors describe retained state

A java.io.NotSerializableException can arise when the Pipeline attempts to save a referenced object that cannot be serialized. The error may surface at a later suspending step rather than the line that originally created the object. Trace which values remain reachable across that boundary. Replacing a parser result with a plain map or extracting the needed scalar before suspension can clarify the contract. Do not lower durability merely to avoid seeing the exception; that can hide a defect while weakening recovery evidence rather than making the state correct.

NonCPS is a narrow computation boundary

A NonCPS method is not transformed in the same way and must not call asynchronous Pipeline steps such as sh, sleep, or input. It is not a speed switch to place around an entire stage. The fragment below contains only a pure size calculation and returns a small value. Its annotation is shown as Jenkins-specific illustrative Groovy and is not compiled here. Review CPS method-mismatch documentation for interactions with constructors, overrides, closures, and non-transformed APIs rather than assuming every language feature behaves identically inside Pipeline.

groovy A pure NonCPS helper illustration
@NonCPS
int countFixtureRows(List rows) {
    return rows.size()
}

The body does not call a Pipeline step or retain external resources. No Groovy compiler, CPS transformer, or Jenkins engine is executed, so this is not a demonstrated runtime fix for a particular exception.

Prefer explicit plain records

The complete local program below turns synthetic rows into a small JSON-compatible summary and demonstrates that an opaque object cannot be represented by that JSON encoder. JSON encoding is not Java serialization or Jenkins CPS persistence, so the exception text and mechanism are deliberately different. The useful design principle is reducing orchestration state to explicit data with known meaning. Large business computations should run in ordinary application tools on an authorized agent, returning bounded results rather than filling the controller’s continuation with every intermediate object.

python A plain-data design model
import json
rows = [{"ok": True}, {"ok": False}, {"ok": True}]
summary = {"total": len(rows), "failed": sum(not r["ok"] for r in rows)}
print(json.dumps(summary, sort_keys=True))
try:
    json.dumps({"opaque": object()})
except TypeError:
    print("opaque value rejected by JSON encoder")
Expected output
{"failed": 1, "total": 3}
opaque value rejected by JSON encoder

This program demonstrates Python JSON behavior only. It does not reproduce NotSerializableException, validate Java serializability, or test Jenkins restart and resumption.

Fix the boundary instead of masking the symptom

When investigating a persistence failure, retain the failing run context, relevant stack excerpt, plugin and library identities, and the smallest state transition that explains it. Avoid dumping serialized program state or credentials into public diagnostics. Review shared helpers that return complex objects and closures capturing more state than expected. After correction, local pure-function tests support the transformation logic, while an actual authorized Pipeline exercise is still needed to establish CPS and restart behavior. Keep those evidence levels separate in release notes and troubleshooting records.

Common errors and fixes

A parser object remains live across a suspending Pipeline step.

Why it happens
The continuation retains a value whose serialization contract is unsuitable.
How to fix it
Extract a small plain result and release the complex object before suspension.
Prevent it next time
Review values captured across step and closure boundaries.

NonCPS wraps a method that calls sh or input.

Why it happens
Non-transformed computation is mixed with CPS Pipeline operations.
How to fix it
Keep the method pure and invoke steps from the proper transformed context.
Prevent it next time
Review supported call directions using CPS mismatch documentation.

Durability is reduced to make a serialization failure disappear.

Why it happens
Less frequent persistence masks the symptom without correcting state.
How to fix it
Fix retained data and evaluate durability as a separate recovery decision.
Prevent it next time
Keep correctness and performance policy reviews distinct.

Check your understanding

1. A failure appears at sleep after a parser ran earlier. Must sleep itself be broken?

Show worked solution

No. The suspension may be where Jenkins tries to persist state containing the earlier object. Trace reachable variables and captured closures, then reduce them to suitable plain data. Preserve the original context instead of changing unrelated sleep timing.

2. Does successful JSON serialization prove a Groovy object will survive a Jenkins restart?

Show worked solution

No. The example tests another serialization system over another language’s values. It illustrates explicit data design only. Jenkins CPS, plugin step resumability, and real persistence need their own appropriate authorized validation.

Key takeaways

  • Persist small explicit orchestration data.
  • NonCPS is for suitable pure computation, not Pipeline step execution.

Official references