Today I’m going to talk about the upcoming feature in Jenkins 1.442 (to be released today), which allows you to finally install plugins without restarting Jenkins.
As I’ll discuss later, internally this is somewhat of an involving work. But for users, this is a very simple feature that doesn’t involve any learning curve. You’ll go to the plugin manager, click “Available” tab, then choose the plugins to install. Scroll all the way down, then you’ll see the “Install without restart” button as well as the “Download new and install after restart” button. The former is the result of this work, allowing you to start using the new plugins right away. The latter is the traditional behaviour, where new plugins take effect after the next restart.
I’m even thinking about removing the latter button after a while, once we gain enough confidence in the feature. I can’t think of any inherent reasons the latter is desirable over the former.
Click the button on the left, and the plugin gets downloaded, installed, and activated:
Whereas existing users would by now be “trained” to click the “restart Jenkins when done” checkbox, now you can just go back to the top page and start using the new features from this plugin right away. Yup, that’s all there is to it.
But how about upgrades?
Unfortunately, because of the architectual choice made in Jenkins, this same scheme wouldn’t allow us to upgrade existing plugins. See below.
But how about uninstallation?
Likewise, this same scheme wouldn’t really let us do the “uninstallation” in the normal sense of the word. But I think some fake of that might be possible — like disabling all the contributions from this plugin (which will prevent anyone from using builders/publishers/etc in newly created jobs), but leaving existing jobs with those features configured as-is, until the next reboot, at which point those features will be just forgotten.
Internal
Jenkins added the plugin support in 1.44, which is some 400 releases ago (man I’ve been doing this for long time!), and since that time Jenkins required a restart for new plugins to take effect. This is because we load plugins at start-up, doing all kinds of computation to build up the immutable data structure around all the plugins. Thus requiring a restart was partly out of my laziness, partly my preference to the immutable data structures, and partly because the computation was rather sequential. And I had bigger fishes to fry, like update centers, and things were left as it was.
Then later came the improvement to the boot up, which removed a long sequential code, and divided them up to the directed acyclic graph of smaller tasks, which we then execute in parallel. This was primarily to speed up the boot time, but this made it somewhat easier to do dynamic initialization as a side effect. Then further down the road, the introduction of Guice made it harder once again, as it prefers immutability, too. So the current implementation puts the new components into the child injector, which seems to make enough plugins happy.
Finally, why is upgrade hard? This is because Jenkins keeps instances of model objects for long time in memory (unlike, say, a typical database application, where those things are request scoped and thus much short-lived.) I like this for a number of reasons, such as more straight-forward object traversals, and the fact that builds take a long time anyway. But as with everything else in the software design, it is not without downsides, and one of them is that it makes dynamic reloading harder. This hopefully answers why just using OSGi alone does not fix this problem.
My current thinking is that “fixing” this via HA is probably the way to go. Or maybe fake uninstallation + dynamic installation might be enough for most plugins. We’ll see.
Conclusion
Hopefully you’ll like this feature. While this is an obvious simple feature for users, as you see internally I’ve done some hard work. If this makes it easier for you to try out new plugins, my time was well spent.