Skip to content

Cache continued tasks during queue maintenance#565

Open
bennettzhu1 wants to merge 1 commit into
jenkinsci:masterfrom
bennettzhu1:upstream-cache-continued-tasks
Open

Cache continued tasks during queue maintenance#565
bennettzhu1 wants to merge 1 commit into
jenkinsci:masterfrom
bennettzhu1:upstream-cache-continued-tasks

Conversation

@bennettzhu1

Copy link
Copy Markdown

Fixes #560

We have seen jenkins queue maintenance spend a lot of time in ContinuedTask.Scheduer.canTake. Queue.maintain already iterates through each buildable and idle executor, and then for each buildable, scans the full buildable queue again to see if any are ContinuedTasks.

To avoid the full buildable loop again, this PR creates a cache of buildable ContinuedTask items. On first pass, populates the map of ContinuedTasks, else it watches the QueueListener to update the cache.

This brings the execution time from (buildable)^2 * idle executors to buildables * idle executors * unique continued tasks. I briefly thought of caching by node to continued task, but it makes updating the cache very messy.

Testing done

We tested it in our staging environment, and we are currently running this patch at my company.

Submitter checklist

  • [ x] Make sure you are opening from a topic/feature/bugfix branch (right side) and not your main branch!
  • [ x] Ensure that the pull request title represents the desired changelog entry
  • [ x] Please describe what you did
  • [ NA] Link to relevant issues in GitHub or Jira
  • [ x] Link to relevant pull requests, esp. upstream and downstream changes
  • [ x] Ensure you have provided tests that demonstrate the feature works or the issue is fixed

Queue maintenance calls QueueTaskDispatcher.canTake for each buildable item and idle executor. ContinuedTask.Scheduler previously scanned every buildable item on each call to find continued tasks, making queue maintenance quadratic in the number of buildables.

Maintain a queue-id keyed cache of buildable continued tasks via QueueListener events. The first scheduler call initializes the cache from the current buildable queue, and later queue transitions add or remove entries. Cache entries keep only a weak task reference plus display name and assigned label so stale entries can be dropped during the next scheduler pass.

Add coverage for a cached task that stops being continued.

Fixes jenkinsci#560
@timja

timja commented Jul 8, 2026

Copy link
Copy Markdown
Member

Cc @jenkinsci/durable-task-plugin-developers

———-

FYI conflicts

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a cache of buildable ContinuedTask items to avoid repeated full-queue scans during Queue.maintain, reducing the complexity of ContinuedTask.Scheduler.canTake.

Changes:

  • Add a ConcurrentHashMap-backed cache of buildable continued tasks, initialized on first use and maintained via a QueueListener.
  • Update Scheduler.canTake to consult the cache instead of rescanning all buildables.
  • Add a regression test ensuring cached tasks are dropped once they are no longer “continued”.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/main/java/org/jenkinsci/plugins/durabletask/executors/ContinuedTask.java Adds/uses a buildable-continued-task cache maintained via QueueListener to speed up canTake.
src/test/java/org/jenkinsci/plugins/durabletask/executors/ContinuedTaskTest.java Adds test coverage verifying the cache evicts tasks that stop being continued.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +94 to +114
private static synchronized void initialize() {
if (INITIALIZED.compareAndSet(false, true)) {
for (Queue.BuildableItem item : Queue.getInstance().getBuildableItems()) {
addIfContinued(item);
}
}
}

private static Iterable<ContinuedItem> values() {
return ITEMS.values();
}

private static synchronized void addIfContinued(Queue.BuildableItem item) {
if (isContinued(item.task)) {
ITEMS.put(item.getId(), new ContinuedItem(item));
}
}

private static synchronized void remove(long id) {
ITEMS.remove(id);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improving CauseOfBlockage during queue maintenance with cache

3 participants