How do I store a Map in a Guava Cache

caching, dictionary, guava, java

Solution

Guava has a different mechanism for "a cache that only ever contains one value"; it's called `Suppliers.memoizeWithExpiration`.

private Supplier<Map<Range<Double>, String> cachedMap = 
    Suppliers.memoizeWithExpiration(
        new Supplier<Map<Range<Double>, String>() {
            public Map<Range<Double>, String> get() {
                return readMappingFromS3();
            }
        }, 10, TimeUnit.MINUTES);

public String getContentLevelForScore(Score score) {
    Map<Range<Double>, String> scoreMap = cachedMap.get();
    // etc.
}

Problem

I had a `Map<Range<Double>, String>` that checks where a particular `Double` value (score) is mapped to a `String` (level). The end users want to be able to dynamically change this mapping, in the long term we would like for there to be a web based `GUI` where they control this but for the short term they're happy for a file to be in `S3` and to editing that whenever a change is needed. I don't want to hit `S3` for each request and want to cache this as it doesn't change too frequently(Once a week or so). I don't want to have to make a code change and bounce my service either. Here is what I have come up with - ``` public class Mapper() { private LoadingCache<Score, String> scoreToLevelCache; public Mapper() { scoreToLevelCache = CacheBuilder.newBuilder() .expireAfterWrite(10, TimeUnit.MINUTES) .build(new CacheLoader<Score, String>() { public String load(Score score) { Map<Range<Double>, String> scoreToLevelMap = readMappingFromS3(); //readMappingFromS3 omitted for brevity for(Range<Double> key : scoreToLevelMap.keySet()) { if(key.contains(score.getCount())) { return scoreToLevelMap.get(key); } } throw new IllegalArgumentException("The score couldn't be mapped to a level. Either the score passed in was incorrect or the mapping is incorrect"); } }); } public String getContentLevelForScore(Score Score) { try { return scoreToLevelCache.get(Score); } catch (ExecutionException e) { throw new InternalServerException(e); } } } ``` The obvious problem with this approach is in the `load` method when I do `Map<Range<Double>, String> scoreToLevelMap = readMappingFromS3();` For each key I'm loading the entire map over and over. This isn't a performance issue but it could become one when the size increases, in any case this is not an efficient approach. I think that keeping this entire map in the cache would be better, but I'm not sure how to do that here. Can anyone help with this or suggest a more elegant way of achieving this.

Original source

Related problems