In cache2k I implemented exactly the behavior you described above. Here is how you build a cache with these features:
CacheBuilder.newCache(Key.class, Value.class)
.name("myCache")
.source(new YourSourceImplementation())
.backgroundRefresh(true)
.suppressExceptions(true)
.maxSize(7777) // maximum entries in the cache
.expiryDuration(60, TimeUnit.SECONDS)
.exceptionExpiryDuration(15, TimeUnit.SECONDS)
.build();
The exipryDuration
is the duration the value is considered valid after it war inserted or modified. The separate setting for exceptionExpiryDuration
is the time until the next refresh is tried after an exception happens.
If an exception happens, but there is no valid entry, the exception is cached and rethrown for the exceptionExpiryDuration
time.
You can also dynamically compute the expiry duration, e.g. based on the exception type. Some more information is in the blog entry About caching exceptions
With backgroundRefresh
an entry is refreshed after it is expired. When no access happens after an refresh within the expiry time, the entry will not get refreshed any more and then eventually evicted.
Unfortunately, I am really behind in documenting all these useful features properly. If you have any more questions you can use the tag cache2k
. If you like some improvements, open an issue on GitHub.
The code works well in our production applications for about a year now. Actually, suppressExceptions
is always the default. It helps very well, e.g. if there is a short network outage.
BTW: Meanwhile, I subsume these semantics under the term cache resiliency.