Just a heads up: On March 24, 2025, starting at 4:30pm CDT / 19:30 UTC, the site will be undergoing scheduled maintenance for a few hours. During this time, the site might be unavailable for a short while. Thanks for your patience.
×Hello!
We have a script rest endpoint that each time that it's called get an information from an external database.
We would like to cache this function since that multiple times the result query returns the same value.
Looking in the internet we have found someways with Guava framework (https://github.com/google/guava) but we would like your help to see if is there any example to this need.
What do you recommend ?
Thanks!
Have you tried to use build-in JIRA Cache functions? https://developer.atlassian.com/confdev/development-resources/confluence-developer-faq/how-do-i-cache-data-in-a-plugin
Not yet, this looks promissor. I'll give it a try!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I'm trying to to use the provided document.
It seems that each time that I call CacheManager.getCache function the cache is erased and created a new one.
I'm testing using the Script Console (feature of Scriptrunner)
import com.atlassian.cache.Cache;
import com.atlassian.cache.CacheLoader;
import com.atlassian.cache.CacheManager;
import com.atlassian.cache.CacheSettingsBuilder;
import com.atlassian.util.concurrent.NotNull;
import com.atlassian.cache.CacheSettings;
import java.util.concurrent.TimeUnit;
import com.atlassian.jira.component.ComponentAccessor;
def CacheManager cacheManager = ComponentAccessor.getComponent(CacheManager.class);
def cache = cacheManager.getCache("com.foo.test.cache",
new ListPagesCacheLoader(),
new CacheSettingsBuilder().expireAfterAccess(5, TimeUnit.MINUTES as TimeUnit).build());
log.error cache.getKeys()
cache.get("hi")
log.error cache.getKeys()
cache.get("hi")
log.error cache.getKeys()
public class ListPagesCacheLoader implements CacheLoader<String, String> {
@Override public String load(@NotNull String value){
sleep(3000);
return value;
}
}
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
It seems that the cache is being recreated each time that I call CacheManager#getCache.
We need this cache stored in disk or memory to each time that the rest endpoint be called we still have the keys into it.
Do you have any tip ?
import com.atlassian.cache.Cache;
import com.atlassian.cache.CacheLoader;
import com.atlassian.cache.CacheManager;
import com.atlassian.cache.CacheSettingsBuilder;
import com.atlassian.util.concurrent.NotNull;
import com.atlassian.cache.CacheSettings;
import java.util.concurrent.TimeUnit;
import com.atlassian.jira.component.ComponentAccessor;
def CacheManager cacheManager = ComponentAccessor.getComponent(CacheManager.class);
def cache = cacheManager.getCache("com.foo.test.cache",
new ListPagesCacheLoader(),
new CacheSettingsBuilder().expireAfterAccess(5, TimeUnit.MINUTES as TimeUnit).build());
log.error cache.getKeys()
cache.get("hi")
log.error cache.getKeys()
cache.get("hi")
log.error cache.getKeys()
public class ListPagesCacheLoader implements CacheLoader<String, String> {
@Override
public String load(@NotNull String value){
sleep(3000);
return value;
}
}
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
As I can see on your example, cache works properly:
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Yes, it did work :)
But if I run the same script again the expected output should get data from cache. But the actual output is the same and I keep getting the first call with 3 seconds delay.
I'm afraid that when I call the getCache method the cache is being flushed.
def cache = cacheManager.getCache("com.foo.test.cache",
new ListPagesCacheLoader(),
new CacheSettingsBuilder().expireAfterAccess(5, TimeUnit.MINUTES as TimeUnit).build());
Do you know how can I define the cache in a way that I can run multiple times the same script without flush the cache's keys? Maybe this happens because I'm running within the script console, this behavior might be different when writing a P2 addon...
Thanks for your help!
Maybe the below picture helps to understand my issue:
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Never mind, I've just tested with Script Endpoint and the cache worked just fine! :D
Thanks!!!!!!!!!!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I wrote a similar test as you have above (for confluence), and noticed the same thing, where the first output of cache.getKeys() is empty. Did you have to do something to resolve this? Or is it a limit of testing via scriptrunner's script console?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
In my case I was indeed creating a new cache with the same key and therefore my cache was flushed. There is another way to retrieve the cached values that I found later.
can you share your test with us? I would like to check and provide you a better answer
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Thank you for responding, especially on a 3 year old question :)
I actually created a question myself that I'll link here for more details. But below is my codebase.
import groovy.json.JsonSlurper
import org.apache.commons.lang3.StringUtils;
import com.atlassian.cache.Cache;
import com.atlassian.cache.CacheLoader;
import com.atlassian.cache.CacheManager;
import com.atlassian.cache.CacheSettings;
import com.atlassian.cache.CacheSettingsBuilder;
import com.atlassian.spring.container.ContainerManager;
import com.atlassian.util.concurrent.NotNull;
import com.atlassian.plugin.spring.scanner.annotation.imports.ConfluenceImport;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
import org.apache.log4j.Logger
import org.apache.log4j.Level
class AppDeploymentsService {
private final Cache<String, Object> cache;
def url
def log
AppDeploymentsService() {
url = new PropertyService('appDeployments').get("app_deployment_url")
}
AppDeploymentsService(def blank) {
def cacheManager = (CacheManager)
ContainerManager.getComponent("cacheManager");
cache = cacheManager.getCache(this.class.getName() + ".cache",
new AppDeploymentsServiceCacheLoader(), new
CacheSettingsBuilder().expireAfterAccess(30,
TimeUnit.MINUTES).build())
url = new PropertyService('appDeployments').get("app_deployment_url")
log = Logger.getLogger(this.class.getName()) log.setLevel(Level.DEBUG)
}
// The loader class used to populate a cache entry on cache miss.
private class AppDeploymentsServiceCacheLoader implements
CacheLoader<String, Object> {
@Override public Object load(@Nonnull String cacheKey) {
return getApplicationInfo(cacheKey)
}
}
def getApplicationInfo(applicationId) {
def uri = "${url}/${applicationId}/deployments"
return new JsonSlurper().parse(new Rest(uri).get())
}
def getAppDeploymentInfo(applicationId) {
// if the appId is not present in the cache then the
// AppDeploymentsServiceCacheLoader
// will create the value as implemented in the 'load' method
// and it will be populated into the cache automatically.
log.debug "cache keys before: ${cache.getKeys()}"
def ret = cache.get(applicationId);
log.debug "cache keys after: ${cache.getKeys()}"
//return evaluate(ret)
return ret
}
}
In case the formatting is off, I will attach a screen shot of my code
Regards,
Erik Macias
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I'm glad to help someone that is having a similar issue that I had in the past :)
Every time you create AppDeploymentsService object, your code will initialize your cache. For the first run that's is fine but when you need to reuse the cache instead of use your existing cache you are replacing the cache with a new empty cache (line 28 in your code).
Also something I noticed during my test is that after the AppDeploymentsService object has been used , the cache seems to be flushed. So I tested using Singleton pattern and it seems to works as expected, I'll share with you now what to change:
1. Add a new attribute in your AppDeploymentsService class:
static AppDeploymentsService appDeploymentsService;
2. Add a new method that will return your singleton object:
public static AppDeploymentsService getAppDeploymentsService(){
if(this.appDeploymentsService == null){
this.appDeploymentsService = new AppDeploymentsService("blank");
}
return this.appDeploymentsService;
}
3. From now on, you should use AppDeploymentsService.getAppDeploymentsService() instead of new getAppDeploymentsService("blank")
See my screenshots here with the output:
PS: I have some issues when I updated the script editor but the console was not reflecting the changes, so I had to use the Scriptrunner Built-in "Clear Groovy class loader", I recommend you to use this whenever you change your AppDeploymentsService groovy;
Hope this helps you,
Italo Qualisoni
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Did it work?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi, just wanted to say thank you - i had that exact same problem and solved it thanks to you.
Cheers,
Leo
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.