Skip to content

Commit

Permalink
feat: add caching support for analytics services and update applicati…
Browse files Browse the repository at this point in the history
…on properties for performance improvements
  • Loading branch information
LeonardoMeireles55 committed Feb 1, 2025
1 parent bcd4407 commit 5891272
Show file tree
Hide file tree
Showing 11 changed files with 56 additions and 23 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
**/*.temp
**/.env*
**/*.env
**/*.gz

# Test and Documentation
**/test/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -62,6 +64,24 @@ public void ensureNameExists(String name) {
}
}


private List<AnalyticsDTO> filterFailedRecords(List<AnalyticsDTO> persistedRecords) {
return persistedRecords.stream().filter(this::isRuleBroken)
.filter(record -> !BLACK_LIST.contains(record.name())).toList();
}

@Async
private void processFailedRecordsNotification(List<AnalyticsDTO> 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<Double> values) {
double sum = values.stream().mapToDouble(Double::doubleValue).sum();
Expand Down Expand Up @@ -162,42 +182,31 @@ public AnalyticsDTO findOneById(Long id) {
}

@Override
@CacheEvict(value = "analyticsByNameAndDateRange", allEntries = true)
public void saveNewAnalyticsRecords(List<AnalyticsDTO> 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<AnalyticsDTO> notPassedList = analyticsList.stream().map(AnalyticMapper::toRecord)
.filter(this::isRuleBroken).toList();

if (notPassedList.isEmpty()) {
return;
}
List<AnalyticsDTO> persistedRecords = analyticsRepository.saveAll(newRecords).stream()
.map(AnalyticMapper::toRecord).toList();

var filteredList = notPassedList.stream()
.filter(notPassed -> !BLACK_LIST.contains(notPassed.name())).toList();
List<AnalyticsDTO> 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<AnalyticsDTO> findAnalytics(Pageable pageable) {
return analyticsRepository.findPaged(pageable);
}

@Cacheable("AnalyticsByNameWithPagination")
@Override
public List<AnalyticsDTO> findAnalyticsByNameWithPagination(Pageable pageable, String name) {
List<AnalyticsDTO> analyticsList =
Expand Down Expand Up @@ -226,6 +235,8 @@ public List<AnalyticsDTO> findAnalyticsByDate(LocalDateTime dateStart, LocalDate
}

@Override
@Cacheable(value = "analyticsByNameAndDateRange",
key = "{#names.hashCode(), #dateStart, #dateEnd, #pageable.pageNumber, #pageable.pageSize}")
public Page<AnalyticsDTO> findAnalyticsByNameInAndDateBetween(List<String> names,
LocalDateTime dateStart, LocalDateTime dateEnd, Pageable pageable) {
return analyticsRepository.findByNameInAndDateBetween(names, dateStart, dateEnd, pageable);
Expand All @@ -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<AnalyticsDTO> values =
Expand All @@ -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<GroupedMeanAndStdByLevelDTO> calculateGroupedMeanAndStandardDeviation(String name,
LocalDateTime startDate, LocalDateTime endDate, Pageable pageable) {
List<AnalyticsDTO> records = analyticsRepository
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -34,6 +35,8 @@ public List<AnalyticsDTO> findAnalyticsByNameAndLevel(Pageable pageable, String
this.convertLevel(level));
}

@Cacheable(value = "analyticsByNameLevelAndDateCache",
key = "{#name, #level, #dateStart, #dateEnd, #pageable.pageNumber, #pageable.pageSize}")
@Override
public List<AnalyticsDTO> findAnalyticsByNameAndLevelAndDate(String name, String level,
LocalDateTime dateStart, LocalDateTime dateEnd, Pageable pageable) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -34,6 +35,8 @@ public List<AnalyticsDTO> findAnalyticsByNameAndLevel(Pageable pageable, String
this.convertLevel(level));
}

@Cacheable(value = "analyticsByNameLevelAndDateCache",
key = "{#name, #level, #dateStart, #dateEnd, #pageable.pageNumber, #pageable.pageSize}")
@Override
public List<AnalyticsDTO> findAnalyticsByNameAndLevelAndDate(String name, String level,
LocalDateTime dateStart, LocalDateTime dateEnd, Pageable pageable) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -33,6 +34,8 @@ public List<AnalyticsDTO> 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<AnalyticsDTO> findAnalyticsByNameAndLevelAndDate(String name, String level,
LocalDateTime dateStart, LocalDateTime dateEnd, Pageable pageable) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package leonardo.labutilities.qualitylabpro.utils.constants;

import java.util.Set;

public class BrokenRules {
public static final Set<String> BROKEN_RULES = Set.of("+3s", "-3s", "+2s", "-2s");
}
2 changes: 2 additions & 0 deletions src/main/resources/application-prod.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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

# ===============================
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 5891272

Please sign in to comment.