Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not use static state variables in Extension class #389

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

gbhat618
Copy link
Contributor

@gbhat618 gbhat618 commented Feb 25, 2025

Flakiness in the test was identified (possibly) due to the use of static stateful fields in the @Extension class. See this comment for details. Issue was likely parallel test execution mutating the state of static field.

This PR addresses the issue following the comment here.

Note: This problem only affects tests and does not impact Jenkins functionality. Whether the data is stored in a static cache or within a field inside a singleton instance, it effectively centralizes event data in one place.

Testing done

  • Automated tests still executing correctly.
  • Manual testing
    • verify the duplicate events monitor functionality is correct as before (tested by sending duplicate events - admin monitor comes, restart jenkins - monitor goes away)

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 demonstrates feature works or fixes the issue

@gbhat618 gbhat618 force-pushed the do-not-use-static-fields-in-extension-class branch from ce9c34f to f016d03 Compare February 25, 2025 01:46
@@ -75,7 +76,7 @@ public void checkRequiredPermission() {
public HttpResponse doGetLastDuplicatePayload() {
Jenkins.get().checkPermission(Jenkins.SYSTEM_READ);
JSONObject data;
var lastDuplicate = DuplicateEventsSubscriber.getLastDuplicate();
var lastDuplicate = ExtensionList.lookupSingleton(DuplicateEventsSubscriber.class).getLastDuplicate();
Copy link
Contributor Author

@gbhat618 gbhat618 Feb 25, 2025

Choose a reason for hiding this comment

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

I'm using ExtensionList.lookupSingleton in two places in this class.

I considered keeping a class-level subscriber field, but that seem to require extra code to manage the reference correctly.

  • Since both this monitor class and the subscriber have @Extension, I think they can't store direct references to each other—perhaps because their initialization order isn't guaranteed?
  • We could store the reference during execution (if (subscriber != null) { subscriber = ExtensionList.lookupSingleton(...); }), but extensions are already looked up in more hot code path - ref code (executed for every event)

So I think this approach is fine - (ExtensionList.lookupSingleton probably doesn't introduce any overhead)

@gbhat618 gbhat618 marked this pull request as ready for review February 25, 2025 02:11
@gbhat618 gbhat618 requested a review from a team as a code owner February 25, 2025 02:11
Comment on lines +211 to +212
return eventTracker.asMap().keySet().stream()
.filter(key -> eventTracker.getIfPresent(key) != null)
Copy link
Member

Choose a reason for hiding this comment

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

BTW I think this would be better written using a stream on entrySet.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

entrySet because it is more performance optimal?

would be like,

return eventTracker.asMap().entrySet().stream()
                   .filter(entry -> entry.getValue() != null)
                   .map(Map.Entry::getKey)
                   .collect(Collectors.toSet());

But since this is a helper method for tests only, and we only need the keys, ig probably keeping on keySet() is easy read.. 🤔

(I now renamed the method to getPresentEventKeys which makes more sense)

Copy link
Member

Choose a reason for hiding this comment

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

Yeah it is just a bad pattern to iterate a map’s key set and then look up the value each time. Does not really matter of course.

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.

2 participants