From 5891272900468b18e846933fa8155b367735a907 Mon Sep 17 00:00:00 2001 From: leonardomeireles55 Date: Sat, 1 Feb 2025 17:47:17 -0300 Subject: [PATCH] feat: add caching support for analytics services and update application properties for performance improvements --- .dockerignore | 1 + .../analytics/AnalyticHelperService.java | 55 ++++++++++++------- .../BiochemistryAnalyticService.java | 3 + .../analytics/CoagulationAnalyticService.java | 3 + .../analytics/HematologyAnalyticService.java | 3 + .../services/users/UserService.java | 1 - .../utils/constants/BrokenRules.java | 7 +++ .../resources/application-prod.properties | 2 + src/main/resources/application.properties | 2 + .../CoagulationAnalyticControllerTest.java | 1 - .../HematologyAnalyticControllerTest.java | 1 - 11 files changed, 56 insertions(+), 23 deletions(-) create mode 100644 src/main/java/leonardo/labutilities/qualitylabpro/utils/constants/BrokenRules.java diff --git a/.dockerignore b/.dockerignore index e1bebb0..b1a7361 100644 --- a/.dockerignore +++ b/.dockerignore @@ -36,6 +36,7 @@ **/*.temp **/.env* **/*.env +**/*.gz # Test and Documentation **/test/ diff --git a/src/main/java/leonardo/labutilities/qualitylabpro/services/analytics/AnalyticHelperService.java b/src/main/java/leonardo/labutilities/qualitylabpro/services/analytics/AnalyticHelperService.java index ff3b1fe..d056fb4 100644 --- a/src/main/java/leonardo/labutilities/qualitylabpro/services/analytics/AnalyticHelperService.java +++ b/src/main/java/leonardo/labutilities/qualitylabpro/services/analytics/AnalyticHelperService.java @@ -7,9 +7,11 @@ import leonardo.labutilities.qualitylabpro.utils.exception.CustomGlobalErrorHandling; import leonardo.labutilities.qualitylabpro.utils.mappers.AnalyticMapper; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.time.LocalDateTime; @@ -62,6 +64,24 @@ public void ensureNameExists(String name) { } } + + private List filterFailedRecords(List persistedRecords) { + return persistedRecords.stream().filter(this::isRuleBroken) + .filter(record -> !BLACK_LIST.contains(record.name())).toList(); + } + + @Async + private void processFailedRecordsNotification(List failedRecords) { + if (!failedRecords.isEmpty()) { + try { + var content = controlRulesValidators.validateRules(failedRecords); + emailService.sendFailedAnalyticsNotification(failedRecords, content); + } catch (Exception e) { + log.error("Error sending identifier notification: {}", e.getMessage()); + } + } + } + // STATISTICS METHODS private MeanAndStdDeviationDTO computeStatistics(List values) { double sum = values.stream().mapToDouble(Double::doubleValue).sum(); @@ -162,42 +182,31 @@ public AnalyticsDTO findOneById(Long id) { } @Override + @CacheEvict(value = "analyticsByNameAndDateRange", allEntries = true) public void saveNewAnalyticsRecords(List valuesOfLevelsList) { - var newAnalytics = valuesOfLevelsList.stream().filter(this::isAnalyticsNonExistent) + var newRecords = valuesOfLevelsList.stream().filter(this::isAnalyticsNonExistent) .map(AnalyticMapper::toEntity).toList(); - if (newAnalytics.isEmpty()) { + if (newRecords.isEmpty()) { log.warn("No new analytics records to save."); throw new CustomGlobalErrorHandling.DataIntegrityViolationException(); } - var analyticsList = analyticsRepository.saveAll(newAnalytics); - - List notPassedList = analyticsList.stream().map(AnalyticMapper::toRecord) - .filter(this::isRuleBroken).toList(); - - if (notPassedList.isEmpty()) { - return; - } + List persistedRecords = analyticsRepository.saveAll(newRecords).stream() + .map(AnalyticMapper::toRecord).toList(); - var filteredList = notPassedList.stream() - .filter(notPassed -> !BLACK_LIST.contains(notPassed.name())).toList(); + List failedRecords = filterFailedRecords(persistedRecords); - try { - var content = controlRulesValidators.validateRules(filteredList); - emailService.sendFailedAnalyticsNotification(filteredList, content); - } catch (Exception e) { - log.error("Error sending identifier notification: {}", e.getMessage()); - } + processFailedRecordsNotification(failedRecords); } - @Override public Page findAnalytics(Pageable pageable) { return analyticsRepository.findPaged(pageable); } + @Cacheable("AnalyticsByNameWithPagination") @Override public List findAnalyticsByNameWithPagination(Pageable pageable, String name) { List analyticsList = @@ -226,6 +235,8 @@ public List findAnalyticsByDate(LocalDateTime dateStart, LocalDate } @Override + @Cacheable(value = "analyticsByNameAndDateRange", + key = "{#names.hashCode(), #dateStart, #dateEnd, #pageable.pageNumber, #pageable.pageSize}") public Page findAnalyticsByNameInAndDateBetween(List names, LocalDateTime dateStart, LocalDateTime dateEnd, Pageable pageable) { return analyticsRepository.findByNameInAndDateBetween(names, dateStart, dateEnd, pageable); @@ -240,6 +251,8 @@ public void deleteAnalyticsById(Long id) { analyticsRepository.deleteById(id); } + @Cacheable(value = "meanAndStdDeviationCache", + key = "{#name, #level, #dateStart, #dateEnd, #pageable.pageNumber, #pageable.pageSize}") public MeanAndStdDeviationDTO calculateMeanAndStandardDeviation(String name, String level, LocalDateTime dateStart, LocalDateTime dateEnd, Pageable pageable) { List values = @@ -248,6 +261,8 @@ public MeanAndStdDeviationDTO calculateMeanAndStandardDeviation(String name, Str return computeStatistics(extractRecordValues(values)); } + @Cacheable(value = "calculateGroupedMeanAndStandardDeviation", + key = "{#name, #level, #dateStart, #dateEnd, #pageable.pageNumber, #pageable.pageSize}") public List calculateGroupedMeanAndStandardDeviation(String name, LocalDateTime startDate, LocalDateTime endDate, Pageable pageable) { List records = analyticsRepository diff --git a/src/main/java/leonardo/labutilities/qualitylabpro/services/analytics/BiochemistryAnalyticService.java b/src/main/java/leonardo/labutilities/qualitylabpro/services/analytics/BiochemistryAnalyticService.java index 6cc6672..5997919 100644 --- a/src/main/java/leonardo/labutilities/qualitylabpro/services/analytics/BiochemistryAnalyticService.java +++ b/src/main/java/leonardo/labutilities/qualitylabpro/services/analytics/BiochemistryAnalyticService.java @@ -5,6 +5,7 @@ import leonardo.labutilities.qualitylabpro.services.email.EmailService; import leonardo.labutilities.qualitylabpro.utils.components.ControlRulesValidators; import leonardo.labutilities.qualitylabpro.utils.exception.CustomGlobalErrorHandling; +import org.springframework.cache.annotation.Cacheable; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @@ -34,6 +35,8 @@ public List findAnalyticsByNameAndLevel(Pageable pageable, String this.convertLevel(level)); } + @Cacheable(value = "analyticsByNameLevelAndDateCache", + key = "{#name, #level, #dateStart, #dateEnd, #pageable.pageNumber, #pageable.pageSize}") @Override public List findAnalyticsByNameAndLevelAndDate(String name, String level, LocalDateTime dateStart, LocalDateTime dateEnd, Pageable pageable) { diff --git a/src/main/java/leonardo/labutilities/qualitylabpro/services/analytics/CoagulationAnalyticService.java b/src/main/java/leonardo/labutilities/qualitylabpro/services/analytics/CoagulationAnalyticService.java index 003111c..af1dcfc 100644 --- a/src/main/java/leonardo/labutilities/qualitylabpro/services/analytics/CoagulationAnalyticService.java +++ b/src/main/java/leonardo/labutilities/qualitylabpro/services/analytics/CoagulationAnalyticService.java @@ -5,6 +5,7 @@ import leonardo.labutilities.qualitylabpro.services.email.EmailService; import leonardo.labutilities.qualitylabpro.utils.components.ControlRulesValidators; import leonardo.labutilities.qualitylabpro.utils.exception.CustomGlobalErrorHandling; +import org.springframework.cache.annotation.Cacheable; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @@ -34,6 +35,8 @@ public List findAnalyticsByNameAndLevel(Pageable pageable, String this.convertLevel(level)); } + @Cacheable(value = "analyticsByNameLevelAndDateCache", + key = "{#name, #level, #dateStart, #dateEnd, #pageable.pageNumber, #pageable.pageSize}") @Override public List findAnalyticsByNameAndLevelAndDate(String name, String level, LocalDateTime dateStart, LocalDateTime dateEnd, Pageable pageable) { diff --git a/src/main/java/leonardo/labutilities/qualitylabpro/services/analytics/HematologyAnalyticService.java b/src/main/java/leonardo/labutilities/qualitylabpro/services/analytics/HematologyAnalyticService.java index 93c9662..0d626a1 100644 --- a/src/main/java/leonardo/labutilities/qualitylabpro/services/analytics/HematologyAnalyticService.java +++ b/src/main/java/leonardo/labutilities/qualitylabpro/services/analytics/HematologyAnalyticService.java @@ -5,6 +5,7 @@ import leonardo.labutilities.qualitylabpro.services.email.EmailService; import leonardo.labutilities.qualitylabpro.utils.components.ControlRulesValidators; import leonardo.labutilities.qualitylabpro.utils.exception.CustomGlobalErrorHandling; +import org.springframework.cache.annotation.Cacheable; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @@ -33,6 +34,8 @@ public List findAnalyticsByNameAndLevel(Pageable pageable, String return findAnalyticsByNameAndLevelWithPagination(pageable, name, convertLevel(level)); } + @Cacheable(value = "analyticsByNameLevelAndDateCache", + key = "{#name, #level, #dateStart, #dateEnd, #pageable.pageNumber, #pageable.pageSize}") @Override public List findAnalyticsByNameAndLevelAndDate(String name, String level, LocalDateTime dateStart, LocalDateTime dateEnd, Pageable pageable) { diff --git a/src/main/java/leonardo/labutilities/qualitylabpro/services/users/UserService.java b/src/main/java/leonardo/labutilities/qualitylabpro/services/users/UserService.java index 92dc0e7..523184e 100644 --- a/src/main/java/leonardo/labutilities/qualitylabpro/services/users/UserService.java +++ b/src/main/java/leonardo/labutilities/qualitylabpro/services/users/UserService.java @@ -16,7 +16,6 @@ import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.AuthenticationException; import org.springframework.stereotype.Service; import java.time.LocalDateTime; diff --git a/src/main/java/leonardo/labutilities/qualitylabpro/utils/constants/BrokenRules.java b/src/main/java/leonardo/labutilities/qualitylabpro/utils/constants/BrokenRules.java new file mode 100644 index 0000000..3c58290 --- /dev/null +++ b/src/main/java/leonardo/labutilities/qualitylabpro/utils/constants/BrokenRules.java @@ -0,0 +1,7 @@ +package leonardo.labutilities.qualitylabpro.utils.constants; + +import java.util.Set; + +public class BrokenRules { + public static final Set BROKEN_RULES = Set.of("+3s", "-3s", "+2s", "-2s"); +} diff --git a/src/main/resources/application-prod.properties b/src/main/resources/application-prod.properties index aa42476..d687599 100644 --- a/src/main/resources/application-prod.properties +++ b/src/main/resources/application-prod.properties @@ -2,6 +2,8 @@ # = SERVER CONFIGURATION # =============================== spring.main.banner-mode=off +spring.main.lazy-initialization=true +server.compression.enabled=false server.error.include-stacktrace=never # =============================== diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 0973c5b..32a4108 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -6,6 +6,8 @@ server.error.include-stacktrace=never spring.profiles.active=${SPRING_PROFILES_ACTIVE:local} spring.output.ansi.enabled=ALWAYS spring.threads.virtual.enabled=true +spring.cache.type=caffeine +spring.cache.caffeine.spec=maximumSize=500, expireAfterWrite=5m # =============================== # = LOGGING CONFIGURATION diff --git a/src/test/java/leonardo/labutilities/qualitylabpro/controllers/CoagulationAnalyticControllerTest.java b/src/test/java/leonardo/labutilities/qualitylabpro/controllers/CoagulationAnalyticControllerTest.java index 3102a18..3e7ee02 100644 --- a/src/test/java/leonardo/labutilities/qualitylabpro/controllers/CoagulationAnalyticControllerTest.java +++ b/src/test/java/leonardo/labutilities/qualitylabpro/controllers/CoagulationAnalyticControllerTest.java @@ -19,7 +19,6 @@ import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.http.MediaType; -import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; diff --git a/src/test/java/leonardo/labutilities/qualitylabpro/controllers/HematologyAnalyticControllerTest.java b/src/test/java/leonardo/labutilities/qualitylabpro/controllers/HematologyAnalyticControllerTest.java index 8f4a93a..38de285 100644 --- a/src/test/java/leonardo/labutilities/qualitylabpro/controllers/HematologyAnalyticControllerTest.java +++ b/src/test/java/leonardo/labutilities/qualitylabpro/controllers/HematologyAnalyticControllerTest.java @@ -19,7 +19,6 @@ import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.http.MediaType; -import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc;