I have implemented this Guava cache in 1 of my SpringBoot service:
public class CachedMenuServiceImpl implements MenuService {
private LoadingCache<String, MenuChart2> menuChartCache = CacheBuilder.newBuilder()
.maximumSize(15)
.expireAfterAccess(15, TimeUnit.MINUTES)
.build(
new CacheLoader<String, MenuChart2>() {
public MenuChart2 load(String menuSymbol) throws ExecutionException {
return generateCharts (menuSymbol);
}
}
);
@Override
public MenuChart2 getCharts (String menuSymbol) throws ExecutionException {
return menuChartCache.get(menuSymbol);
}
public MenuChart2 generateCharts (String MenuSymbol) throws ExecutionException {
MenuChart2 menuChart2;
…
return menuChart2;
}
public MenuChart2 generateCharts (String menuSymbol) throws ExecutionException {
if (LOG.isDebugEnabled()) {
LOG.debug ("generating charts for {} ", menuSymbol);
}
Menu menu = findBySymbol(menuSymbol).get();
Map<LocalDate, MenuChart2.Statistics> last30DPerDay =
menuPriceByDayService.findAllOrderByUpdateDate(menu, DateUtils.monthlyDate(), 30)
.stream()
.sorted(comparing(MenuPriceByDay::getUpdateDate))
.collect(Collectors
.toMap(MenuPriceByDay::getUpdateLocalDate, p -> new MenuChart2().new Statistics( p.getMinPrice().doubleValue(),
p.getMaxPrice().doubleValue(),
p.getAvgPrice().doubleValue())));
Map<LocalDate, MenuChart2.Statistics> last3MPerDay =
menuPriceByDayService.findAllOrderByUpdateDate(menu, DateUtils.quarterlyDate(), 92)
.stream()
.sorted(comparing(MenuPriceByDay::getUpdateDate))
.collect(Collectors
.toMap(MenuPriceByDay::getUpdateLocalDate, p -> new MenuChart2().new Statistics( p.getMinPrice().doubleValue(),
p.getMaxPrice().doubleValue(),
p.getAvg;
Map<LocalDate, MenuChart2.Statistics> last6MPerDay =
menuPriceByDayService.findAllOrderByUpdateDate(menu, DateUtils.semestralDate(), 26)
.stream()
.sorted(comparing(MenuPriceByDay::getUpdateDate))
.collect(Collectors
.toMap(MenuPriceByDay::getUpdateLocalDate, p -> new MenuChart2().new Statistics( p.getMinPrice().doubleValue(),
p.getMaxPrice().doubleValue(),
p.getAvgPrice().doubleValue())));
Map<LocalDate, MenuChart2.Statistics> last1YPerDay =
menuPriceByDayService.findAllOrderByUpdateDate(menu, DateUtils.yearlylDate(), 52)
.stream()
.sorted(comparing(MenuPriceByDay::getUpdateDate))
.collect(Collectors
.toMap(MenuPriceByDay::getUpdateLocalDate, p -> new MenuChart2().new Statistics( p.getMinPrice().doubleValue(),
p.getMaxPrice().doubleValue(),
p.getAvgPrice().doubleValue())));
Map<LocalDateTime, DoubleSummaryStatistics> priceStatisticsXhour =
menuPriceService.findAll(menu).parallelStream()
.filter(cp -> cp.getUpdateDate().after(DateUtils.yesterday()))
.sorted(comparing(MenuPrice::getUpdateDate))
.collect(Collectors.groupingBy(cp -> cp.getUpdateLocalDateHour(),
Collectors.summarizingDouble(cp -> cp.getPriceInDouble())))
.entrySet().parallelStream().sorted(Map.Entry.comparingByKey())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
(oldValue, newValue) -> oldValue, LinkedHashMap::new));
MenuChart2 menuChart2 = new MenuChart2();
menuChart2.setLas24HPerHour (priceStatisticsXhour);
menuChart2.setLast30DPerDay (last30DPerDay);
menuChart2.setLast1YPerDay (last1YPerDay);
menuChart2.setLast3MPerDay (last3MPerDay);
menuChart2.setLast6MPerDay (last6MPerDay);
return menuChart2;
}
but when I access to it I got this error:
com.google.common.util.concurrent.UncheckedExecutionException: java.lang.IllegalStateException: Duplicate key com.tdk.api.json.MenuChart2$Statistics@6f377595
at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2218)
at com.google.common.cache.LocalCache.get(LocalCache.java:4147)
at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:4151)
at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:5140)
at com.tdk.backend.service.CachedMenuServiceImpl.getCharts(CachedMenuServiceImpl.java:189)
at com.tdk.backend.service.CachedMenuServiceImpl$$FastClassBySpringCGLIB$$2ea84be7.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
...
Caused by: java.lang.IllegalStateException: Duplicate key com.tdk.api.json.MenuChart2$Statistics@6f377595
at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
at java.util.HashMap.merge(HashMap.java:1254)
at java.util.stream.Collectors.lambda$toMap$58(Collectors.java:1320)
at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:352)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at com.tdk.backend.service.CachedMenuServiceImpl.generateCharts(CachedMenuServiceImpl.java:238)
at com.tdk.backend.service.CachedMenuServiceImpl$1.load(CachedMenuServiceImpl.java:86)
at com.tdk.backend.service.CachedMenuServiceImpl$1.load(CachedMenuServiceImpl.java:84)
at com.google.common.cache.LocalCache$LoadingValueReference.loadFuture(LocalCache.java:3708)
at com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2416)
at com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2299)
at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2212)
... 101 common frames omitted
The exception doesn't appear to be coming from the Guava cache. It appears to be coming from you using a stream and collecting it into a map. The exception is occurring when you call .get(..)
because it is a LoadingCache
, so when it runs the code in the generate method, the exception is generated. You are probably doing something like
.stream().collect(
Collectors.toMap(o -> someFunctionToGetKey(),
o -> someFunctionToGetValue())
);
In this implementation however, you are not providing logic on what to happen if two of the same keys are generated. You need to specify a third parameter which defines the behavior to take if a duplicate key is encountered. For example, consider we have this basic pojo
@Data
class Model {
private int id;
private String name;
}
And we have List<Model> list = new ArrayList<>();
You can then do:
list.stream()
.collect(Collectors.toMap(
Model::getId,
Model::getName,
(v1,v2) -> v2
));
The third parameter, (v2,v2) -> v2
instructs the collector on what to do if there are two entries in the list with the same id. In this example, it is just using the most recent value of name
for the key id
.