From 99393dbddf1ae34e9fb32d22224faf57f2b2f4a4 Mon Sep 17 00:00:00 2001 From: Michael Breu <michael.breu@uibk.ac.at> Date: Mon, 27 May 2024 11:03:37 +0000 Subject: [PATCH] =?UTF-8?q?Resolve=20"Sharing=20Plattform=20st=C3=BCrzt=20?= =?UTF-8?q?jeden=20Tag=20um=2010=20Uhr=20ab,"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jpa/PersistenceAuditEventRepository.java | 13 +++-- .../gitsearch/service/AuditEventService.java | 47 ++++++++++++++---- .../service/AuditEventServiceIT.java | 49 +++++++++++++++---- 3 files changed, 88 insertions(+), 21 deletions(-) diff --git a/src/main/java/at/ac/uibk/gitsearch/repository/jpa/PersistenceAuditEventRepository.java b/src/main/java/at/ac/uibk/gitsearch/repository/jpa/PersistenceAuditEventRepository.java index 1c05967ba..58b58527e 100644 --- a/src/main/java/at/ac/uibk/gitsearch/repository/jpa/PersistenceAuditEventRepository.java +++ b/src/main/java/at/ac/uibk/gitsearch/repository/jpa/PersistenceAuditEventRepository.java @@ -3,10 +3,11 @@ package at.ac.uibk.gitsearch.repository.jpa; import at.ac.uibk.gitsearch.domain.PersistentAuditEvent; import java.time.Instant; import java.util.List; -import java.util.stream.Stream; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; /** * Spring Data JPA repository for the {@link PersistentAuditEvent} entity. @@ -18,7 +19,13 @@ public interface PersistenceAuditEventRepository extends JpaRepository<Persisten Page<PersistentAuditEvent> findAllByAuditEventDateBetween(Instant fromDate, Instant toDate, Pageable pageable); - List<PersistentAuditEvent> findByAuditEventDateBefore(Instant before); + List<PersistentAuditEvent> findTop1000ByAuditEventDateBefore(Instant before); - Stream<PersistentAuditEvent> streamByAuditEventDateBefore(Instant before); + /** + * @Deprecated due to https://hibernate.atlassian.net/browse/HHH-5528 + */ + @Deprecated + @Modifying + @Query("delete from PersistentAuditEvent ae where ae.auditEventDate < ?1") + int deleteInBulkByRoleId(Instant before); } diff --git a/src/main/java/at/ac/uibk/gitsearch/service/AuditEventService.java b/src/main/java/at/ac/uibk/gitsearch/service/AuditEventService.java index 46a7a696e..50491cb67 100644 --- a/src/main/java/at/ac/uibk/gitsearch/service/AuditEventService.java +++ b/src/main/java/at/ac/uibk/gitsearch/service/AuditEventService.java @@ -1,9 +1,11 @@ package at.ac.uibk.gitsearch.service; +import at.ac.uibk.gitsearch.domain.PersistentAuditEvent; import at.ac.uibk.gitsearch.repository.jpa.AuditEventConverter; import at.ac.uibk.gitsearch.repository.jpa.PersistenceAuditEventRepository; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.List; import java.util.Optional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -12,7 +14,12 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionCallback; +import org.springframework.transaction.support.TransactionTemplate; import tech.jhipster.config.JHipsterProperties; /** @@ -24,22 +31,29 @@ import tech.jhipster.config.JHipsterProperties; @Transactional public class AuditEventService { - private final Logger log = LoggerFactory.getLogger(AuditEventService.class); + @SuppressWarnings("unused") + private static final Logger LOGGER = LoggerFactory.getLogger(AuditEventService.class); private final JHipsterProperties jHipsterProperties; private final PersistenceAuditEventRepository persistenceAuditEventRepository; + private final TransactionTemplate isolatedTransactionTemplate; + private final AuditEventConverter auditEventConverter; public AuditEventService( PersistenceAuditEventRepository persistenceAuditEventRepository, AuditEventConverter auditEventConverter, - JHipsterProperties jhipsterProperties + JHipsterProperties jhipsterProperties, + PlatformTransactionManager platformTransactionManager ) { this.persistenceAuditEventRepository = persistenceAuditEventRepository; this.auditEventConverter = auditEventConverter; this.jHipsterProperties = jhipsterProperties; + + isolatedTransactionTemplate = new TransactionTemplate(platformTransactionManager); + isolatedTransactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); } /** @@ -49,13 +63,28 @@ public class AuditEventService { */ @Scheduled(cron = "0 0 23 * * ?") public void removeOldAuditEvents() { - persistenceAuditEventRepository - .streamByAuditEventDateBefore(Instant.now().minus(jHipsterProperties.getAuditEvents().getRetentionPeriod(), ChronoUnit.DAYS)) - .forEach(auditEvent -> { - log.debug("Deleting audit data {}", auditEvent); - persistenceAuditEventRepository.delete(auditEvent); - persistenceAuditEventRepository.flush(); - }); + boolean next = true; + + while (next) { + next = + isolatedTransactionTemplate.execute( + new TransactionCallback<Boolean>() { + @Override + public Boolean doInTransaction(TransactionStatus status) { + final List<PersistentAuditEvent> auditEventDeleteCandidates = persistenceAuditEventRepository.findTop1000ByAuditEventDateBefore( + Instant.now().minus(jHipsterProperties.getAuditEvents().getRetentionPeriod(), ChronoUnit.DAYS) + ); + persistenceAuditEventRepository.deleteAll(auditEventDeleteCandidates); + return !auditEventDeleteCandidates.isEmpty(); + } + } + ); + } + // not working, due to https://hibernate.atlassian.net/browse/HHH-5528 + // int count = persistenceAuditEventRepository.deleteInBulkByRoleId( + // Instant.now().minus(jHipsterProperties.getAuditEvents().getRetentionPeriod(), ChronoUnit.DAYS) + // ); + // LOGGER.info("cleaned up {} outdated AuditEnvets", count); } @Transactional(readOnly = true) diff --git a/src/test/java/at/ac/uibk/gitsearch/service/AuditEventServiceIT.java b/src/test/java/at/ac/uibk/gitsearch/service/AuditEventServiceIT.java index 0b0d49432..b8f418552 100644 --- a/src/test/java/at/ac/uibk/gitsearch/service/AuditEventServiceIT.java +++ b/src/test/java/at/ac/uibk/gitsearch/service/AuditEventServiceIT.java @@ -13,9 +13,17 @@ import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionCallback; +import org.springframework.transaction.support.TransactionTemplate; import tech.jhipster.config.JHipsterProperties; /** @@ -25,6 +33,11 @@ import tech.jhipster.config.JHipsterProperties; @Transactional public class AuditEventServiceIT { + private final Logger log = LoggerFactory.getLogger(AuditEventServiceIT.class); + + @Autowired + private PlatformTransactionManager platformTransactionManager; + @Autowired private AuditEventService auditEventService; @@ -40,13 +53,16 @@ public class AuditEventServiceIT { private PersistentAuditEvent auditEventNew; + private static final int MAX_OUTDATEDVALUES = 4500; + @BeforeEach public void init() { + log.info("Preparing {} outdated AuditEvents", MAX_OUTDATEDVALUES); auditEventsOld = new ArrayList<>(); - for (int i = 1; i <= 1000; i++) { + for (int i = 1; i <= MAX_OUTDATEDVALUES; i++) { PersistentAuditEvent auditEventOld = new PersistentAuditEvent(); auditEventOld.setAuditEventDate( - Instant.now().minus(jHipsterProperties.getAuditEvents().getRetentionPeriod() + i, ChronoUnit.DAYS) + Instant.now().minus((jHipsterProperties.getAuditEvents().getRetentionPeriod() + 1) * 24 + i, ChronoUnit.HOURS) ); auditEventOld.setPrincipal("test-user-old"); auditEventOld.setAuditEventType("test-type"); @@ -54,6 +70,7 @@ public class AuditEventServiceIT { auditEventOld.getData().put("someDate", "someValue" + i); auditEventsOld.add(auditEventOld); } + log.info("Prepared {} outdated AuditEvents", MAX_OUTDATEDVALUES); auditEventWithinRetention = new PersistentAuditEvent(); auditEventWithinRetention.setAuditEventDate( Instant.now().minus(jHipsterProperties.getAuditEvents().getRetentionPeriod() - 1, ChronoUnit.DAYS) @@ -69,16 +86,30 @@ public class AuditEventServiceIT { @Test @Timeout(value = 5, unit = TimeUnit.MINUTES) - @Transactional + @DirtiesContext public void verifyOldAuditEventsAreDeleted() { - persistenceAuditEventRepository.deleteAll(); - auditEventsOld.forEach(auditEventOld -> persistenceAuditEventRepository.save(auditEventOld)); - persistenceAuditEventRepository.save(auditEventWithinRetention); - persistenceAuditEventRepository.save(auditEventNew); + TransactionTemplate isolatedTransactionTemplate = new TransactionTemplate(platformTransactionManager); + isolatedTransactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); + + isolatedTransactionTemplate.execute( + new TransactionCallback<Boolean>() { + @Override + public Boolean doInTransaction(TransactionStatus status) { + persistenceAuditEventRepository.deleteAll(); + + auditEventsOld.forEach(auditEventOld -> persistenceAuditEventRepository.save(auditEventOld)); + persistenceAuditEventRepository.save(auditEventWithinRetention); + persistenceAuditEventRepository.save(auditEventNew); + return true; + } + } + ); + + log.info("Saved {} outdated AuditEvents", MAX_OUTDATEDVALUES); + assertThat(persistenceAuditEventRepository.findAll().size()).isGreaterThan(MAX_OUTDATEDVALUES); - persistenceAuditEventRepository.flush(); auditEventService.removeOldAuditEvents(); - persistenceAuditEventRepository.flush(); + // persistenceAuditEventRepository.flush(); assertThat(persistenceAuditEventRepository.findAll().size()).isEqualTo(2); assertThat(persistenceAuditEventRepository.findByPrincipal("test-user-old")).isEmpty(); -- GitLab