Jenkins CPS Serialization Visualization
Normal Execution (No Restart)
Pipeline Execution
│
▼
┌─────────────────┐
│ Stage 1: Build │ ✓ Complete
└─────────────────┘
│
▼
┌─────────────────┐
│ Stage 2: Test │ ✓ Complete
└─────────────────┘
│
▼
┌─────────────────┐
│ Stage 3: Deploy │ ✓ Complete
└─────────────────┘
│
▼
SUCCESS
When Jenkins Restart Occurs
Pipeline Execution
│
▼
┌─────────────────┐
│ Stage 1: Build │ ✓ Complete
└─────────────────┘
│
▼
┌─────────────────┐
│ Stage 2: Test │ ⏳ Running...
└─────────────────┘
│
│ 💥 Jenkins Restart!
│
▼
╔═══════════════════════════════════════════════════════════════╗
║ 1. Serialize ║
║ ║
║ Memory State: Disk (program.dat): ║
║ ┌─────────────────────┐ ┌─────────────────────┐ ║
║ │ currentStage = 2 │ ──────▶ │ 01001010 01100101 │ ║
║ │ testResult = null │ Save │ 01101110 01101011 │ ║
║ │ buildArtifact = ... │ │ 01101001 01101110 │ ║
║ └─────────────────────┘ └─────────────────────┘ ║
║ ║
╚═══════════════════════════════════════════════════════════════╝
│
│ 🔄 Jenkins Restart Complete
│
▼
╔═══════════════════════════════════════════════════════════════╗
║ 2. Deserialize ║
║ ║
║ Disk (program.dat): Memory Restored: ║
║ ┌─────────────────────┐ ┌─────────────────────┐ ║
║ │ 01001010 01100101 │ ──────▶ │ currentStage = 2 │ ║
║ │ 01101110 01101011 │ Restore │ testResult = null │ ║
║ │ 01101001 01101110 │ │ buildArtifact = ... │ ║
║ └─────────────────────┘ └─────────────────────┘ ║
║ ║
╚═══════════════════════════════════════════════════════════════╝
│
▼
┌─────────────────┐
│ Stage 2: Test │ ◀── Resume from here!
└─────────────────┘
│
▼
┌─────────────────┐
│ Stage 3: Deploy │
└─────────────────┘
│
▼
SUCCESS
Serializable vs Non-Serializable
┌─────────────────────────────────────────────────────────────────┐
│ Serializable ✓ │
│ (Can be saved/restored) │
│ │
│ String name = "build-123" ──▶ Save ──▶ Restore ──▶ OK │
│ int count = 5 ──▶ Save ──▶ Restore ──▶ OK │
│ List<String> files = [...] ──▶ Save ──▶ Restore ──▶ OK │
│ Map<String, Object> config ──▶ Save ──▶ Restore ──▶ OK │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Non-Serializable ✗ │
│ (Cannot be saved/restored) │
│ │
│ Socket connection = ... ──▶ Save ──▶ 💥 ERROR │
│ InputStream stream = ... ──▶ Save ──▶ 💥 ERROR │
│ Database db = ... ──▶ Save ──▶ 💥 ERROR │
│ Iterator iter = ... ──▶ Save ──▶ 💥 ERROR │
│ │
│ Reason: "Current connection state" cannot be saved │
│ to a file and restored later │
│ │
└─────────────────────────────────────────────────────────────────┘
Application in This Codebase
┌─────────────────────────────────────────────────────────────────┐
│ HttpApiService (src/service/) │
│ │
│ class HttpApiService { │
│ def jenkinsfile ◀── Must be Serializable │
│ def logger ◀── Must be Serializable │
│ │
│ def executeRequest(...) { │
│ HttpClient httpClient = HttpClients.createDefault() │
│ // ▲ │
│ // │ │
│ // Local variable - OK │
│ // Disappears when method ends │
│ // Jenkins doesn't need to save it │
│ } │
│ } │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ shellScriptHelper (vars/) │
│ │
│ def call(Closure shellScriptClosure, List args = []) { │
│ sh(shMap) ◀── Direct pipeline step call │
│ } │ │
│ │ │
│ └── Must be in vars/ to call pipeline │
│ steps from CPS context │
│ │
└─────────────────────────────────────────────────────────────────┘
Pipeline Step Execution Context
The second CPS constraint: Where the pipeline step is called matters.
┌─────────────────────────────────────────────────────────────────┐
│ Rules │
│ │
│ vars/ → direct pipeline step call ✓ OK │
│ src/ → direct pipeline step call ✗ Not allowed │
│ src/ → vars/ wrapper → pipeline step ✓ OK │
│ │
└─────────────────────────────────────────────────────────────────┘
Pattern: Indirect Call via vars/
┌─────────────────────────────────────────────────────────────────┐
│ │
│ src/HttpApiService.groovy (Outside CPS context) │
│ ┌─────────────────────────────────────────┐ │
│ │ logger.stepProcessing("msg") │ │
│ │ │ │ │
│ │ ▼ │ │
│ └─────────┼───────────────────────────────┘ │
│ │ │
│ │ Call │
│ ▼ │
│ vars/logger.groovy (Inside CPS context) │
│ ┌─────────────────────────────────────────┐ │
│ │ void stepProcessing(String description) │ │
│ │ echo "🏃 ${description}" ✓ OK │ │
│ │ ▲ │ │
│ │ │ │ │
│ │ echo executes here │ │
│ └─────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
What Doesn't Work
┌─────────────────────────────────────────────────────────────────┐
│ │
│ src/HttpApiService.groovy (Outside CPS context) │
│ ┌─────────────────────────────────────────┐ │
│ │ jenkinsfile.sh("echo hello") ✗ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 💥 Problem │ │
│ │ │ │
│ │ sh() called directly from src/ │ │
│ │ → Pipeline step outside CPS context │ │
│ │ → Jenkins cannot handle properly │ │
│ └─────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Correct Pattern in This Codebase
// vars/logger.groovy (Inside CPS context)
void stepProcessing(String description) {
echo "🏃 ${description}" // ✓ echo called here - OK
}
// src/HttpApiService.groovy (Outside CPS context)
class HttpApiService {
def logger
HttpApiService(def jenkinsfile) {
this.logger = jenkinsfile.logger
}
def doSomething() {
logger.stepProcessing("msg") // ✓ Delegates to vars/ - OK
// jenkinsfile.sh("...") // ✗ Direct call - Not allowed
}
}
Component Placement
| Component |
Location |
Reason |
| HttpApiService |
src/service/ |
Apache HttpClient (pure Java). logger called indirectly via vars/ |
| shellScriptHelper |
vars/ |
Direct sh() call |
| bitbucketApiHelper |
vars/ |
Direct withCredentials() call |
| logger |
vars/ |
Direct echo call |
Summary
| Constraint |
Description |
| Serializable |
Objects must be savable as bytes for restoration after restart |
| Pipeline Step Context |
sh, echo, withCredentials, etc. can only be called directly from vars/ |
| Scenario |
Result |
| All variables are Serializable |
Pipeline continues after Jenkins restart ✓ |
| Non-Serializable variable exists |
NotSerializableException error 💥 |
| Direct pipeline step call from src/ |
CPS problem 💥 |
| Indirect call via vars/ wrapper from src/ |
OK ✓ |