Hej. Mam wystawione API które zbiera od użytkowników pewne dane a po X czasie (np. co minutę) zapisuje w bazie statystyki odnośnie tych danych. W skrócie wygląda to tak:
Endpoint:
@Bean
RouterFunction<ServerResponse> saveTime() {
return route(POST("/times"),
req -> req.body(toMono(Timestamp.class))
.subscribe(service::add)
.then(ok().build()));
}
Service grupuje dane na mapie:
private Map<Integer, List<LocalTime>> stats = new ConcurrentHashMap<>();
void add(Timestamp timestamp) {
if (stats .containsKey(timestamp.getId())) {
stats .get(timestamp.getId()).add(timestamp.getTimestamp());
} else {
stats .put(timestamp.getId(), new ArrayList<>(Collections.singleton(timestamp.getTimestamp())));
}
}
Problem w tym że to nie zawsze działa poprawnie. Np. Kiedy co jakiś czas (np. co 10 min) uruchamiany jest job który pobiera tę listę i zapisuje statystyki do bazy to też przy okazji ją czyści żeby na nowo zbierać dane o timestampach. W chwili w której przychodzi request i jednocześnie uruchamia się cron job leci NPE w miejscu stats .get(timestamp.getId()).add(timestamp.getTimestamp());
co w sumie ma sens bo lista mogła zostać już wyczyszczona.
Mam więc 2 pytania:
- Jak to poprawnie powinno być zaimplementowane - nie chodzi mi o konkretną implementację ale chociaż podpowiedź bo myślałem że
ConcurrentHashMap
rozwiąże problem z dostępem do tej kolekcji. - Czy mój endpoint który w sumie jest banalny ale czy z pkt widzenia async jest okej? Wcześniej pisałem wszystko na tomcata i w sumie Webfluxa trochę dzięki @jarekr000000 się uczę który poleca go zamiast Spring Boota. Ps. Oczywiście wiem że jeszcze można całkowicie te adnotacje wyrzucić i mam zamiar to zrobić;)