-
Bug
-
Resolution: Unresolved
-
Minor
-
None
We have observed intermittent errors when dynamically installing a plugin because one of the extension in the new plugin is somehow registered twice when using ExtensionList.lookupSingleton. This leads to exceptions like this:
java.lang.IllegalStateException: Expected 1 instance of XYZ but got 2 at hudson.ExtensionList.lookupSingleton(ExtensionList.java:457) ...
Restarting Jenkins fixes the issue.
This was originally observed with a proprietary CloudBees plugin but jgreffe was able to create an isolated test case.
Here is my understanding of the conditions required to trigger the issue:
- If there is an ExtensionListListener that watches for some extension type (such as APeriodicWorkExtensionListListener)
- And you dynamically install a plugin which has an extension that listener cares about (e.g. CustomAPeriodicWork)
- And the listener actually invokes some method on the changed extensions (such as APeriodicWork.getInitialDelay)
- And that method on the extension from the plugin looks up another extension defined in the dynamically-installed plugin (e.g. CustomAPeriodicWork.getRecurrencePeriod calls ExtensionList.lookupSingleton(CustomExtension.class))
- And Jenkins.refreshExtensions refreshes the extension list that the listener is watching before the extension list for the other extension used by the first extension (Not quite clear to me how Jenkins.extensionLists gets updated, but I guess this is what makes the issue intermittent)
- Then ExtensionList.load gets called for the second extension before Jenkins.refreshExtensions has refreshed that extension list (because it must be loaded directly when the first extension looks it up), causing ExtensionList.refresh to insert a second entry for the exact same extension instance into its list.
- This causes later calls to ExtensionList.lookupSingleton(CustomExtension.class) to fail, because there are 2 entries in the list for the same object
I see two relatively straightforward fixes:
- The one currently proposed in the PR: Refresh all extension lists before calling any extension listeners, so that if the listeners do anything that will require one of the new extensions to be loaded, it will happen after the lists have been refreshed.
- Modify ExtensionList.refresh to check for duplicates instead of using .addAll
Perhaps a deeper fix should be made as well, for example maybe all extension lookups need to be blocked during Jenkins.refreshExtensions until all of the lists have been refreshed, but I am not sure.
- relates to
-
JENKINS-71129 IllegalStateException: Expected 1 instance of org.jenkinsci.plugins.resourcedisposer.AsyncResourceDisposer but got 2
- Open
- links to