expiringmap
expiringmap copied to clipboard
Lazy loaded entry `null` is still cached, for a short period
I am trying to implement a 'near-cache' for an expensive Database query using ExpiringMap. The following example is a gross simplification, but should describe the gist of it. In the real, more complex implementation the expiration time is different for each entry and there is some more logic associated with each entry which is not relevant for this issue.
ExpiringMap.builder()
.variableExpiration()
.expiringEntryLoader((ExpiringEntryLoader<String, DBItem>) id -> {
DBItem obj = Database.findById(id);
return (obj != null) ? new ExpiringValue<>(obj, 5, TimeUnit.MINUTES) : null;
})
.build();
This works fine for when the item is found in the database. The first time the DB is hit and the item is cached for 5 minutes, while all future calls within that duration only hit the ExpiringMap cache.
However, if the object is not found in the DB, then null
is still cached. Even weirder, it seems to be cached for some short default duration that I did not define anywhere (1 minute?). Is there a way to avoid this behavior and prevent a value from being stored in the map? In case of null
, I would like each subsequent get
call to hit the database again.
Even weirder, it seems to be cached for some short default duration that I did not define anywhere (1 minute?)
This sounds like a bug
Is there a way to avoid this behavior and prevent a value from being stored in the map?
That would change the behavior/contract of ExpiringEntryLoader
, but I think it's a fair change that null
should be a distinguished return value that causes an entry not to be loaded. Alternative, we could have some other distinguished return value, such as ExpiringValue.NONE
that one could return in such cases (while still allowing null).
Happy to take a PR to fix these.