You rang? Yes, I think I can help, here.
Short answer: Since you aren’t using a
CacheLoader, you are getting the exact same cache back every time, and it will still contain objects from your previous lifecycle. This difference is perhaps not called out as clearly as it could be, but the CacheFactory JavaDocs do talk about it. There is no way to make such a cache behave correctly in JIRA Data Center, so we strongly encourage you to use the lazy-loading approach instead. If for some reason you can’t do that, the only alternative is to clear the cache yourself when your plugin starts or stops.
To understand why these behave so different requires a bit of a history lesson. The atlassian-cache library already existed in both JIRA and Confluence when we began working on JIRA Data Center in the 6.2 development cycle. JIRA never had made much use of it, preferring to use Guava caches throughout its code, but Confluence used atlassian-cache heavily, but the library didn’t at that stage have any of the lazy-load patterns that we determined were going to be the safest approach for caches to use in the Data Center offering. The main reason for preferring lazy-load caches is that their correctness is much easier to reason about – you don’t have to worry about two nodes making changes to the same thing at once and what state the cache ends up in as a result, because all caches treat the database as the final authority and the only information you replicate is an invalidation message – the nodes reload from the database and redundant invalidations don’t cause any permanent harm.
But caches from plugins cause a couple of
ClassLoader issues that we have to contend with:
CacheLoader that the cache uses for lazy-loading is provided by the plugin. This has to be behind a
WeakReference or it will prevent the cache data from being reclaimed.
Cache itself contains data classes that were likely provided by the plugin. This should be behind a
WeakReference as well so that the cache data can be freed when the plugin. Unfortunately…
JIRA saw that this was going to be a problem for non-lazy-load caches too and initially made all of the caches behave this way, where the cache data itself was always behind a weak reference so that it could be reclaimed when the plugin was unloaded. But this change of behaviour was too much for Confluence to accept. They had the (terrible, in my opinion) pattern of calling
cacheFactory.getCache("foo") multiple times throughout the classes that were using them instead of calling it once and holding onto it. Putting the cache behind a
WeakReference broke this access pattern because the cache contents kept disappearing on them, and they weren’t willing to change all the access points in Confluence to stop doing this (plus, there were compatibility concerns for third-party Confluence plugins).
So we had to put it back the way that it was. On the new APIs with a
CacheLoader, we did the right thing, but on the old ones that don’t take a loader, the cache is held via a strong reference, so it survives a plugin unload.
- If you can, switch to using a
CacheLoader. That is the safest model to use and all of these concerns are addressed automatically.
- If you can’t do that, then explicitly clear the cache immediately after you obtain it and as you shut down to guarantee that it isn’t polluted with data from before your plugin loaded and doesn’t stick around indefinitely. This isn’t ideal for a clustered environment, as it will negatively impact other nodes, but it’s better than doing nothing.