Skip to content

Notify GraphListener extensions prior to listeners added via FlowExecution.addListener#1052

Merged
dwnusbaum merged 12 commits into
jenkinsci:masterfrom
dwnusbaum:reverse-listener-orders
Jul 21, 2025
Merged

Notify GraphListener extensions prior to listeners added via FlowExecution.addListener#1052
dwnusbaum merged 12 commits into
jenkinsci:masterfrom
dwnusbaum:reverse-listener-orders

Conversation

@dwnusbaum

@dwnusbaum dwnusbaum commented Jun 30, 2025

Copy link
Copy Markdown
Member

See also jenkinsci/workflow-job-plugin#548, which can only be safely merged and released after this PR.

While investigating whether I could change a synchronous GraphListener to an asynchronous one in jenkinsci/opentelemetry-plugin#700, I noticed that WorklowRun.GraphL currently always runs before GraphListener extensions. This means that asynchronous listeners may observe nodes that occur towards the end of the build after the build has already completed, which seems undesirable.

This PR adjusts the listener notification order so that GraphListener extensions are handled first, and then listeners added via FlowExecution.addListener are notified in the reverse order that they were added. This PR also starts setting TimingAction directly on FlowStartNode so that timing is always set on all nodes for all listeners and we can clean up some logic in WorkflowRun$GraphL, see jenkinsci/workflow-job-plugin#548.

(Earlier, I also considered jenkinsci/workflow-api-plugin#410 and jenkinsci/workflow-job-plugin#547, but those more complicated changes seem to be unnecessary if we just handle TimingAction for FlowStartNode here).

Given that the only known issue with Workflow$GraphL completing the build before other async listeners run is with a hypothetical change to opentelemetry that cannot happen for other unrelated reasons, I am not sure if we actually care about going through with this change, but I wanted to look into it just in case it matters for other listeners in the future.

Testing done

See new automated tests.

Submitter checklist

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

@dwnusbaum dwnusbaum changed the title Run local GraphListeners after GraphListener extensions Notify listeners in order according to new GraphListener.ordinal method Jul 1, 2025
@dwnusbaum dwnusbaum changed the title Notify listeners in order according to new GraphListener.ordinal method Notify GraphListener extensions prior to listeners added via FlowExecution.addListener Jul 2, 2025
}
execution.flowStartNodeActions.clear();
} // may be unset from loadProgramFailed
n.addAction(new TimingAction());

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We handle TimingAction here not just to be able to clean up GraphL in jenkinsci/workflow-job-plugin#548, but so that listeners in plugins like datadog and github-autostatus (and probably others) which expect TimingAction to be present when they run continue to work even for FlowStartNode. They should already work fine for all other nodes, which go through setNewHead down below, which has been adding TimingAction since #188.

listeners = new CopyOnWriteArrayList<>();
}
listeners.add(listener);
listeners.add(0, listener);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It seemed preferable to handle the reversal of this list when listeners are added rather than each time listeners are notified. In practice I think this list will normally only contain WorkflowRun$GraphL and WorkflowRun$NodePrintListener, although WorkflowRun$FailOnLoadListener will be added temporarily.

var p = r.createProject(WorkflowJob.class);
p.setDefinition(new CpsFlowDefinition("echo 'test'", true));
var b = r.buildAndAssertSuccess(p);
await().until(() -> listener.done);

@dwnusbaum dwnusbaum Jul 2, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

With the current code, this await is unnecessary, since the listener always runs before the build completes, but you need the await to reproduce the problematic case when reverting the changes to src/main, since otherwise the build (and thus the test) complete before the listener even runs.

@dwnusbaum dwnusbaum marked this pull request as ready for review July 3, 2025 15:12
@dwnusbaum dwnusbaum requested a review from a team July 3, 2025 15:13
@dwnusbaum

Copy link
Copy Markdown
Member Author

@jglick Do you think there is still value in doing this even though it doesn't actually matter for any known GraphListener implementations? I originally looked into this in the context of making a synchronous listener in opentelemetry asynchronous (jenkinsci/opentelemetry-plugin#700 (comment)), but there are other unrelated problems which prevent that change.

Comment thread plugin/src/main/java/org/jenkinsci/plugins/workflow/cps/CpsFlowExecution.java Outdated

@jglick jglick left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It seems generally desirable, so if you think this is safe to do, we may as well.

Co-authored-by: Jesse Glick <jglick@cloudbees.com>
@dwnusbaum

Copy link
Copy Markdown
Member Author

Ok, I'll try to run it through the PCT first.

@dwnusbaum

Copy link
Copy Markdown
Member Author

I ran the PCT (using a system internal to CloudBees) and things looked ok, so I'll merge this.

@dwnusbaum dwnusbaum merged commit b7e492a into jenkinsci:master Jul 21, 2025
17 checks passed
@dwnusbaum dwnusbaum deleted the reverse-listener-orders branch July 21, 2025 18:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants