I am evaluating some results with Spring SpEL and I would like to cache those results so I don't have to evaluate expressions with same parameters.
My cached key object:
@Data
@AllArgsConstructor
public class CachedResult {
private String baseName;
private Interval interval;
public boolean isBetweenInclusive(final DateTime date) {
return interval.contains(date) || interval.getEnd().isEqual(date);
}
}
My solution to find a record with interval
that covers the given dateTime
:
public String getEvaluatedResult(final String baseName, final DateTime dateTime) {
return cache.asMap().entrySet()
.stream()
.filter(entry -> entry.getKey().getBaseName().equals(baseName) && entry.getKey().isBetweenInclusive(dateTime))
.findFirst()
.map(Map.Entry::getValue)
.orElse(null);
}
I wanted to make use of cache.get(key, valueLoader)
method so that it can put values in the cache itself if needed, but I can't think of a way to make use of isBetweenInclusive
and this method.
My attempt with a comment where I got into problems:
public class MyCache {
private final Cache<CachedResult, String> cache;
public DefaultRolloverCache(final int expiration) {
this.cache = CacheBuilder.newBuilder()
.expireAfterWrite(expiration, TimeUnit.MINUTES)
.build();
}
public String getFileName(final String baseName, final DateTime dateTime, final Callable<String> valueLoader) {
try {
return cache.get(new CachedResult(baseName, null/*How to find an interval that covers the given dateTime?*/), valueLoader);
} catch (final ExecutionException e) {
throw new IllegalArgumentException(String.format("Cannot read fileName from cache with basename: '%s' and dateTime: %s", baseName, dateTime), e);
}
}
}
I call this method like:
cache.getFileName(baseName, new DateTime(), () -> doSlowCalculations(baseName));
Ofcourse, since I don't know how to use the mentioned method, I have to use cache.put(new CachedResult(...))
to put records in the cache myself.
Is there a better way to filter the Cache than calling asMap
and filtering it like a Map? Can I somehow use the cache.get(key, valueLoader)
or even Guavas CacheLoader
so that it can put values in automatically?
As the performance goes, I will have at maximum 5-10 records in it at one time, but I will read a lot from it, so read times are very important to me and I'm not sure if my current implementation that iterates over 5-10 records all the time and checks each of them is the best approach.
My final solution after reading the comment Louis wrote:
public String getFileName(final String baseName, final DateTime dateTime, final Supplier<String> valueLoader) {
final Optional<String> cached = cache.asMap().entrySet()
.stream()
.filter(entry -> entry.getKey().getBaseName().equals(baseName) && entry.getKey().isBetweenInclusive(dateTime))
.findFirst()
.map(Map.Entry::getValue);
if (cached.isPresent()) {
return cached.get();
} else {
final String evaluatedValue = valueLoader.get();
cache.put(new CachedResult(baseName, intervalCalculator.getInterval(dateTime)), evaluatedValue);
return evaluatedValue;
}
}