1

I'm working on some back-end service which is asynchronous in nature. That is, we have multiple jobs that are ran asynchronously and results are written to some record.

This record is basically a class wrapping an HashMap of results (keys are job_id).

The thing is, I don't want to calculate or know in advance how many jobs are going to run (if I knew, I could cache.invalidate() the key when all the jobs has already been completed)

Instead, I'd like to have the following scheme:

  1. Set an expiry for new records (i.e. expireAfterWrite)
  2. On expiry, write (actually upsert) the record the database
  3. If a cache miss occurs, load() is called to fetch the record from the database (if not found, create a new one)

The problem: I tried to use Caffeine cache but the problem is that records aren't expired at the exact time they were supposed to. I then read this SO answer for Guava's Cache and I guess a similar mechanism works for Caffeine as well.

So the problem is that a record can "wait" in the cache for quite a while, even though it was already completed. Is there a way to overcome this issue? That is, is there a way to "encourage" the cache to invalidate expired items?

That lead me to question my solution. Would you consider my solution a good practice?

P.S. I'm willing to switch to other caching solutions, if necessary.

IsaacLevon
  • 2,260
  • 4
  • 41
  • 83
  • 2
    Caffeine, like Guava, does not create threads so it cannot schedule work outside of user activity. Both would require calling `cleanUp` periodically. `CacheWriter` would provide safer semantics for write-behind, though. Scheduling semantics could do done via a Java 9 feature ([#195](https://github.com/ben-manes/caffeine/issues/195)) and I'll prioritize looking into providing interfaces to take advantage of that while being JDK8 compatible. – Ben Manes Jul 15 '19 at 16:07
  • 2
    That is possible in [cache2k](https://cache2k.org) by adding a synchronous expiry listener e.g. `builder.Cache2kBuilder.addListener(new CacheEntryExpiredListener() ...)` and keeping sharp expiry off with `builder.sharpExpiry(false)`. However, cache2k uses one thread per cache for the expiration, that could become a bottleneck, if you need more than one expiry operation in parallel on the database. Your use case is a good example. I will keep that in mind for further development directions. – cruftex Jul 16 '19 at 12:26
  • 1
    @yaseco This capability has been released in Caffeine 2.8. Simply specify `Caffeine.scheduler(Scheduler.systemScheduler())` (if JDK9+) in your cache builder and it will remove the expired entry promptly. – Ben Manes Aug 06 '19 at 05:50

1 Answers1

1

You can have a look at the Ehcache with write-behind. It is for sure more setup effort but it is working quite well

sudo
  • 747
  • 6
  • 19
  • I don't think Ehcache has scheduled expiration with write-behind. Expiration is implemented lazily by polluting the cache with dead entries and relying on size eviction to discard them eventually. – Ben Manes Jul 15 '19 at 16:03