Course outline

Jenkins / Persistent state and controlled delivery / Lesson 16

Job concurrency, resource locks, and stale work

Coordinate the actual shared resource while independently checking candidate freshness.

About 4 min readContent reviewed
On this page

Lesson goals

  • Distinguish per-job serialization from cross-job resource coordination.
  • Explain why a lock cannot by itself prevent an older candidate from adopting later.

Per-job concurrency has a limited scope

Declarative disableConcurrentBuilds can serialize runs of the same Pipeline job under its documented options. That does not universally coordinate every job targeting one shared environment. Parcel’s release job and a separate maintenance job may both affect the same target despite having different names. First identify the shared resource and all writers, then select an appropriate coordination mechanism. Aborting a previous run is another policy with external-state implications; it is not equivalent to patiently queuing every required transition or undoing work already accepted elsewhere.

A resource lock coordinates a named boundary

The Lockable Resources plugin provides lock steps under its installed configuration and permissions. A resource name should represent the actual shared target or capability, not a unique run identifier that never collides. Avoid a global lock that blocks unrelated environments unnecessarily. The fragment below is illustrative and only prints a label inside a hypothetical lock. No plugin is installed, resource configured, lock acquired, or target touched. Resource coordination also needs a documented owner and behavior for cancellation, abandoned work, and unavailable resources.

groovy A resource-lock scope illustration
lock(resource: "parcel-staging-lab") {
    echo "A real operation would still require candidate and target checks"
}

The resource and plugin are synthetic prerequisites. This fragment is not a deployment or a complete Pipeline and establishes no actual mutual exclusion. A lock protects only writers that correctly participate in the same coordination contract.

Mutual exclusion does not determine release order

Candidate r8 may finish testing before slower r7. If r8 adopts first and r7 later obtains the lock, serialization alone can regress the target. Recheck eligibility after waiting against authoritative state or a reviewed ordering policy. The Milestone plugin can help discard older runs at designated points under its specific same-job semantics, but it is not a cross-system transaction or a substitute for target-side generation checks. Required migration sequences may need every transition, while replaceable stateless candidates may permit supersession. State which model applies.

Make freshness and duplicate intent visible

The complete local model rejects a declared older generation and recognizes a repeated operation identity. It uses sequential in-memory state, not concurrent transactions or a Jenkins plugin. A real implementation must make relevant checks and updates atomic under its target’s guarantees and account for crashes or fencing. Do not infer chronology by sorting labels such as r9 and r10 as text. The numeric generation here is an explicitly declared ordering input, not a universal interpretation of a Jenkins build number across different jobs.

python A sequential coordination model
current = 8
seen = set()
def request(identity, generation):
    global current
    if identity in seen:
        return "duplicate"
    if generation < current:
        return "stale"
    current = generation
    seen.add(identity)
    return "accepted"
print(request("lab-one", 7))
print(request("lab-two", 9))
print(request("lab-two", 9))
Expected output
stale
accepted
duplicate

The program neither acquires a Jenkins lock nor implements distributed idempotency. It illustrates declared invariants using synthetic generations and request names.

Coordinate cleanup and uncertain outcomes too

If a lock-protected job loses its response after submission, the external operation may continue after the Pipeline stops. Observe the original request before retrying or releasing application-level ownership assumptions. Cleanup must not remove a shared resource now used by another run. Keep submission, convergence, verification, and reporting separate so a report failure does not trigger another adoption. Retain the resolved resource identity and candidate with the decision record. A concurrency setting is useful only when every participating path respects its scope and recovery contract.

Common errors and fixes

disableConcurrentBuilds is assumed to lock an environment across unrelated jobs.

Why it happens
Job-local serialization was mistaken for resource-wide coordination.
How to fix it
Identify every writer and choose a shared reviewed resource contract.
Prevent it next time
Document coordination scope and ownership explicitly.

A newer candidate is overwritten by an older waiting run.

Why it happens
The lock prevents overlap but does not establish freshness.
How to fix it
Recheck eligibility after waiting using authoritative ordering state.
Prevent it next time
Review supersession and required-transition policies separately.

A unique build identifier is used as the resource-lock name.

Why it happens
Every writer requests a different resource and never coordinates.
How to fix it
Use the actual shared target identity as the boundary.
Prevent it next time
Test intended collisions and unrelated parallel cases in design review.

Check your understanding

1. Two jobs target the same environment but each disables its own concurrent builds. Is cross-job overlap prevented?

Show worked solution

Not by that setting alone. Each job serializes its own runs. A shared resource or target-side coordination mechanism must cover all relevant writers, with appropriate authority and recovery behavior.

2. Can a milestone guarantee safety for an external migration after submission?

Show worked solution

No. Its documented run-order behavior does not undo accepted external work or guarantee state compatibility. Preserve request identity, observe the target, and follow the migration’s ordered transition and recovery contract.

Key takeaways

  • Coordinate all writers of the actual resource.
  • Check overlap, freshness, duplicate intent, and recovery independently.

Official references