I'm using Hazelcast 3.12 jCache implementation. My cache has been declared declaratively (via hazelcast-config.xml), however I want to be able to change the ExpiryPolicy's duration dynamically, as the timeout value is only available programatically.
I've looked at the docs for ICache but do not see any methods that would allow me to retreive and/or modify the expiry policy for the cache as a whole.
My cache is declared as:
<cache name="toto">
<async-backup-count>1</async-backup-count>
<backup-count>1</backup-count>
<cache-entry-listeners>
<cache-entry-listener old-value-required="true">
<cache-entry-listener-factory class-name="cache.UserRolesEntryListenerStaticFactory"/>
</cache-entry-listener>
</cache-entry-listeners>
<key-type class-name="java.lang.String"/>
<management-enabled>true</management-enabled>
<statistics-enabled>true</statistics-enabled>
<quorum-ref>1</quorum-ref>
<partition-lost-listeners></partition-lost-listeners>
</cache>
and I would like to set/update the expiry policy when it is retrieved as:
@Produces
@Singleton
@UserRolesCache
public Cache<String, String[]> createUserRoleCache(@HazelcastDistributed CacheManager cacheManager) {
Cache cache = cacheManager.getCache("toto");
// get expiry timeout from a 3rd service
int timeout = configService.getCacheExpiry();
// how to set the expiry policy here???
// cache.setExpiryPolicy(.....) ?????
}
Is this feasible using the jCache or Hazelcast API?
There is no explicit way to change the default expiry policy of a Cache
(either in JCache or
Hazelcast-specific APIs).
You can achieve the same effect by configuring your Cache
with a custom Factory<ExpiryPolicy>
.
In your ExpiryPolicy
implementation you can consult your external service to query the current
expiry duration and return that so the JCache implementation applies it. Notice that since the expiry policy
is queried on each entry creation/access/update, it is best that ExpiryPolicy
methods implementation do not involve
any remote service queries or database access, otherwise you will experience high latency. For example,
it is best to register your expiry policy as a listener to your external service (if supported)
or have a separate executor to schedule queries to the external service for updates.
Example implementation using JCache API:
class Scratch {
public static void main(String[] args)
throws InterruptedException {
MutableConfiguration<String, String[]> roleCacheConfig = new MutableConfiguration<>();
// other config here...
roleCacheConfig.setExpiryPolicyFactory(new CustomExpiryPolicyFactory());
CachingProvider provider = Caching.getCachingProvider();
CacheManager manager = provider.getCacheManager();
Cache<String, String[]> cache = manager.createCache("test", roleCacheConfig);
cache.put("a", new String[]{}); // consults getExpiryForCreation
Thread.sleep(1000);
cache.get("a"); // consults getExpiryForAccess
Thread.sleep(1000);
cache.put("a", new String[]{}); // consults getExpiryForUpdate
}
public static class CustomExpiryPolicyFactory implements Factory<ExpiryPolicy>, Serializable {
@Override
public ExpiryPolicy create() {
// initialize custom expiry policy: at this point
// the custom expiry policy should be registered as listener to
// external service publishing expiry info or otherwise configured
// to poll the external service for new expiry info
CustomExpiryPolicy expiryPolicy = new CustomExpiryPolicy(120, 30, 120);
return expiryPolicy;
}
}
public static class CustomExpiryPolicy implements ExpiryPolicy {
private volatile Duration expiryForCreation;
private volatile Duration expiryForAccess;
private volatile Duration expiryForUpdate;
public CustomExpiryPolicy(long secondsForCreation,
long secondsForAccess,
long secondsForUpdate) {
this.expiryForCreation = new Duration(TimeUnit.SECONDS, secondsForCreation);
this.expiryForAccess = new Duration(TimeUnit.SECONDS, secondsForAccess);
this.expiryForUpdate = new Duration(TimeUnit.SECONDS, secondsForUpdate);
}
// assuming this is invoked from external service whenever there is a change
public void onExpiryChanged(long secondsForCreation,
long secondsForAccess,
long secondsForUpdate) {
this.expiryForCreation = new Duration(TimeUnit.SECONDS, secondsForCreation);
this.expiryForAccess = new Duration(TimeUnit.SECONDS, secondsForAccess);
this.expiryForUpdate = new Duration(TimeUnit.SECONDS, secondsForUpdate);
}
@Override
public Duration getExpiryForCreation() {
return expiryForCreation;
}
@Override
public Duration getExpiryForAccess() {
return expiryForAccess;
}
@Override
public Duration getExpiryForUpdate() {
return expiryForUpdate;
}
}
}
You can supply your custom expiry policy factory class name in declarative Hazelcast XML config like this:
<cache name="toto">
<async-backup-count>1</async-backup-count>
<backup-count>1</backup-count>
...
<expiry-policy-factory class-name="com.example.cache.CustomExpirePolicyFactory" />
...
</cache>
As a side-note, there are methods in ICache
, the Hazelcast-specific extended Cache
interface, that allow
you to perform operations on a key or set of keys with a custom expiry policy specified per-key (but not change the cache-wide applicable expiry policy).