jhalterman / expiringmap

A high performance thread-safe map that expires entries
Apache License 2.0
1.01k stars 142 forks source link

Lazy loaded entry `null` is still cached, for a short period #64

Open cdekker opened 5 years ago

cdekker commented 5 years ago

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.

jhalterman commented 5 years ago

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.