I am using caffeine in the following configuration:
Cache<String, String> cache = Caffeine.newBuilder()
.executor(newWorkStealingPool(15))
.scheduler(createScheduler())
.expireAfterWrite(10, TimeUnit.SECONDS)
.maximumSize(MAXIMUM_CACHE_SIZE)
.removalListener(this::onRemoval)
.build();
private Scheduler createScheduler() {
return forScheduledExecutorService(newSingleThreadScheduledExecutor());
}
will I be correct to assume that onRemoval
method will be executed on the newWorkStealingPool(15)
ForkJoinPool, and the scheduler will be invoked only to find the expired entries that needs to be evicted?
meaning it will go something like this:
newWorkStealingPool(15)
define in the cache builder?I didn't found documentation that explains this behavior, so I am asking here
Tnx
Your assumption is close, except that it is slightly more optimized in practice.
Caffeine.executor
to call Cache.cleanUp
.Caffeine.executor
to call RemovalListener.onRemoval
.Caffeine.executor
to call Cache.cleanUp
(see #3).The scheduler does the minimal amount of work and any processing is deferred to the executor. That maintenance work is cheap due to using O(1) algorithms so it may occur often based on the usage activity. It is optimized for small batches of work, so the enforced ~1s delay between scheduled calls helps capture more work per invocation. If the next expiration event is in the distant future then the scheduler won't run until then, though calling threads may trigger a maintenance cycle due to their activity on the cache (see #1,2).