From 196339cc39e2c488b0b75836fb36a758d368c596 Mon Sep 17 00:00:00 2001 From: Jinil Sung Date: Wed, 8 Nov 2023 13:18:54 -0800 Subject: [PATCH] GRAD2-2395: task is complete GRAD2-2395: task is complete --- .../batchgraduation/config/AsyncConfig.java | 12 +-- .../batchgraduation/config/BatchConfig.java | 2 +- .../config/BatchJobConfig.java | 55 ++++++------- .../config/BatchJobLauncher.java | 1 + .../config/TaskSchedulerConfig.java | 2 +- .../entity/BatchJobExecutionEntity.java | 5 +- .../entity/BatchStepExecutionEntity.java | 19 ++--- .../BatchStepExecutionRepository.java | 14 ++++ .../service/GradDashboardService.java | 80 +++++++++++++------ .../service/GradDashboardServiceTest.java | 57 ++++++++++--- 10 files changed, 161 insertions(+), 86 deletions(-) create mode 100644 api/src/main/java/ca/bc/gov/educ/api/batchgraduation/repository/BatchStepExecutionRepository.java diff --git a/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/config/AsyncConfig.java b/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/config/AsyncConfig.java index 084cce6a..4c66469d 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/config/AsyncConfig.java +++ b/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/config/AsyncConfig.java @@ -1,24 +1,18 @@ package ca.bc.gov.educ.api.batchgraduation.config; -import ca.bc.gov.educ.api.batchgraduation.util.EducGradBatchGraduationApiConstants; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; +import org.springframework.core.task.SimpleAsyncTaskExecutor; import org.springframework.core.task.TaskExecutor; import org.springframework.scheduling.annotation.EnableAsync; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @Configuration @EnableAsync @Profile("!test") public class AsyncConfig { @Bean(name = "asyncExecutor") - public TaskExecutor asyncExecutor(EducGradBatchGraduationApiConstants constants) { - ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); - executor.setCorePoolSize(constants.getNumberOfPartitions()); - executor.setMaxPoolSize(constants.getNumberOfPartitions()); - executor.setThreadNamePrefix("async-"); - executor.initialize(); - return executor; + public TaskExecutor asyncExecutor() { + return new SimpleAsyncTaskExecutor("async-"); } } diff --git a/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/config/BatchConfig.java b/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/config/BatchConfig.java index c3c53202..51afdca1 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/config/BatchConfig.java +++ b/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/config/BatchConfig.java @@ -29,7 +29,7 @@ public class BatchConfig { public JobLauncher asyncJobLauncher(JobRepository jobRepository) throws Exception { TaskExecutorJobLauncher jobLauncher = new TaskExecutorJobLauncher(); jobLauncher.setJobRepository(jobRepository); - jobLauncher.setTaskExecutor(new SimpleAsyncTaskExecutor()); + jobLauncher.setTaskExecutor(new SimpleAsyncTaskExecutor("asyncTask-")); jobLauncher.afterPropertiesSet(); return jobLauncher; } diff --git a/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/config/BatchJobConfig.java b/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/config/BatchJobConfig.java index 0946217c..67bb0bba 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/config/BatchJobConfig.java +++ b/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/config/BatchJobConfig.java @@ -23,8 +23,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.SimpleAsyncTaskExecutor; import org.springframework.core.task.TaskExecutor; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionException; @@ -87,7 +87,7 @@ public Step masterStepRegGrad(JobRepository jobRepository, EducGradBatchGraduati .partitioner(graduationJobStep(jobRepository, skipListener).getName(), partitionerRegGrad()) .step(graduationJobStep(jobRepository, skipListener)) .gridSize(constants.getNumberOfPartitions()) - .taskExecutor(taskExecutor(constants)) + .taskExecutor(taskExecutor()) .build(); } @@ -97,7 +97,7 @@ public Step masterStepRegGradError(JobRepository jobRepository, EducGradBatchGra .partitioner(graduationJobErrorStep(jobRepository, skipListener).getName(), partitionerRegGradRetry()) .step(graduationJobErrorStep(jobRepository, skipListener)) .gridSize(constants.getNumberOfPartitions()) - .taskExecutor(taskExecutor(constants)) + .taskExecutor(taskExecutor()) .build(); } @@ -107,7 +107,7 @@ public Step masterStepRegGradErrorRetry(JobRepository jobRepository, EducGradBat .partitioner(graduationJobErrorRetryStep(jobRepository, skipListener).getName(), partitionerRegGradRetry()) .step(graduationJobErrorRetryStep(jobRepository, skipListener)) .gridSize(constants.getNumberOfPartitions()) - .taskExecutor(taskExecutor(constants)) + .taskExecutor(taskExecutor()) .build(); } @@ -230,7 +230,7 @@ public Step masterStepTvrRun(JobRepository jobRepository, EducGradBatchGraduatio .partitioner(tvrJobStep(jobRepository,skipListener).getName(), partitionerTvrRun()) .step(tvrJobStep(jobRepository,skipListener)) .gridSize(constants.getNumberOfPartitions()) - .taskExecutor(taskExecutor(constants)) + .taskExecutor(taskExecutor()) .build(); } @@ -240,7 +240,7 @@ public Step masterStepTvrRunError(JobRepository jobRepository, EducGradBatchGrad .partitioner(tvrJobErrorStep(jobRepository,skipListener).getName(), partitionerTvrRunRetry()) .step(tvrJobErrorStep(jobRepository,skipListener)) .gridSize(constants.getNumberOfPartitions()) - .taskExecutor(taskExecutor(constants)) + .taskExecutor(taskExecutor()) .build(); } @@ -250,7 +250,7 @@ public Step masterStepTvrRunErrorRetry(JobRepository jobRepository, EducGradBatc .partitioner(tvrJobErrorRetryStep(jobRepository,skipListener).getName(), partitionerTvrRunRetry()) .step(tvrJobErrorRetryStep(jobRepository,skipListener)) .gridSize(constants.getNumberOfPartitions()) - .taskExecutor(taskExecutor(constants)) + .taskExecutor(taskExecutor()) .build(); } @@ -362,7 +362,7 @@ public Step masterStepSpcRegGrad(JobRepository jobRepository, EducGradBatchGradu .partitioner(slaveSpcRegGradStep(jobRepository,skipListener).getName(), partitionerSpcRegGrad()) .step(slaveSpcRegGradStep(jobRepository,skipListener)) .gridSize(constants.getNumberOfPartitions()) - .taskExecutor(taskExecutor(constants)) + .taskExecutor(taskExecutor()) .build(); } @@ -372,7 +372,7 @@ public Step masterStepSpcRegGradError(JobRepository jobRepository, EducGradBatch .partitioner(slaveSpcRegGradErrorStep(jobRepository,skipListener).getName(), partitionerSpcRegGradRetry()) .step(slaveSpcRegGradErrorStep(jobRepository,skipListener)) .gridSize(constants.getNumberOfPartitions()) - .taskExecutor(taskExecutor(constants)) + .taskExecutor(taskExecutor()) .build(); } @@ -382,7 +382,7 @@ public Step masterStepSpcRegGradErrorRetry(JobRepository jobRepository, EducGrad .partitioner(slaveSpcRegGradErrorRetryStep(jobRepository,skipListener).getName(), partitionerSpcRegGradRetry()) .step(slaveSpcRegGradErrorRetryStep(jobRepository,skipListener)) .gridSize(constants.getNumberOfPartitions()) - .taskExecutor(taskExecutor(constants)) + .taskExecutor(taskExecutor()) .build(); } @@ -491,7 +491,7 @@ public Step masterStepSpcTvrRun(JobRepository jobRepository, EducGradBatchGradua .partitioner(slaveStepSpcTvrRun(jobRepository,skipListener).getName(), partitionerSpcRegGrad()) .step(slaveStepSpcTvrRun(jobRepository,skipListener)) .gridSize(constants.getNumberOfPartitions()) - .taskExecutor(taskExecutor(constants)) + .taskExecutor(taskExecutor()) .build(); } @@ -501,7 +501,7 @@ public Step masterStepSpcTvrRunError(JobRepository jobRepository, EducGradBatchG .partitioner(slaveStepSpcTvrRunError(jobRepository,skipListener).getName(), partitionerSpcRegGradRetry()) .step(slaveStepSpcTvrRunError(jobRepository,skipListener)) .gridSize(constants.getNumberOfPartitions()) - .taskExecutor(taskExecutor(constants)) + .taskExecutor(taskExecutor()) .build(); } @@ -511,7 +511,7 @@ public Step masterStepSpcTvrRunErrorRetry(JobRepository jobRepository, EducGradB .partitioner(slaveStepSpcTvrRunErrorRetry(jobRepository,skipListener).getName(), partitionerSpcRegGradRetry()) .step(slaveStepSpcTvrRunErrorRetry(jobRepository,skipListener)) .gridSize(constants.getNumberOfPartitions()) - .taskExecutor(taskExecutor(constants)) + .taskExecutor(taskExecutor()) .build(); } @@ -669,7 +669,7 @@ public Step masterStepDisRun(JobRepository jobRepository, EducGradBatchGraduatio .partitioner(slaveStepDisRun(jobRepository).getName(), partitionerDisRun()) .step(slaveStepDisRun(jobRepository)) .gridSize(constants.getNumberOfPartitions()) - .taskExecutor(taskExecutor(constants)) + .taskExecutor(taskExecutor()) .build(); } @@ -701,7 +701,7 @@ public Step masterStepDisRunYearly(JobRepository jobRepository, EducGradBatchGra .partitioner(slaveStepDisRun(jobRepository).getName(), partitionerDisRunYearly()) .step(slaveStepDisRunYearly(jobRepository)) .gridSize(constants.getNumberOfPartitions()) - .taskExecutor(taskExecutor(constants)) + .taskExecutor(taskExecutor()) .build(); } @@ -727,7 +727,7 @@ public Step masterStepDisRunYearlyNonGrad(JobRepository jobRepository, EducGradB .partitioner(slaveStepDisRunYearlyNonGradByMincode(jobRepository).getName(), partitionerDisRunYearlyNonGrad()) .step(slaveStepDisRunYearlyNonGradByMincode(jobRepository)) .gridSize(constants.getNumberOfPartitions()) - .taskExecutor(taskExecutor(constants)) + .taskExecutor(taskExecutor()) .build(); } @@ -753,7 +753,7 @@ public Step masterStepDisRunSupplemental(JobRepository jobRepository, EducGradBa .partitioner(slaveStepDisRun(jobRepository).getName(), partitionerDisRunSupplemental()) .step(slaveStepDisRun(jobRepository)) .gridSize(constants.getNumberOfPartitions()) - .taskExecutor(taskExecutor(constants)) + .taskExecutor(taskExecutor()) .build(); } @@ -785,7 +785,7 @@ public Step masterStepUserReqDisRun(JobRepository jobRepository, EducGradBatchGr .partitioner(slaveStepDisRun(jobRepository).getName(), partitionerDisRunUserReq()) .step(slaveStepDisRun(jobRepository)) .gridSize(constants.getNumberOfPartitions()) - .taskExecutor(taskExecutor(constants)) + .taskExecutor(taskExecutor()) .build(); } @@ -847,7 +847,7 @@ public Step masterStepBlankUserReqDisRun(JobRepository jobRepository, EducGradBa .partitioner(slaveStepBlankDisRun(jobRepository).getName(), partitionerDisRunBlankUserReq()) .step(slaveStepBlankDisRun(jobRepository)) .gridSize(constants.getNumberOfPartitions()) - .taskExecutor(taskExecutor(constants)) + .taskExecutor(taskExecutor()) .build(); } @@ -910,7 +910,7 @@ public Step masterStepPsiUserReqDisRun(JobRepository jobRepository, EducGradBatc .partitioner(slaveStepPsiDisRun(jobRepository).getName(), partitionerDisRunPsiUserReq()) .step(slaveStepPsiDisRun(jobRepository)) .gridSize(constants.getNumberOfPartitions()) - .taskExecutor(taskExecutor(constants)) + .taskExecutor(taskExecutor()) .build(); } @@ -958,7 +958,7 @@ public Step masterStepCertRegen(JobRepository jobRepository, EducGradBatchGradua .partitioner(certRegenJobStep(jobRepository, skipListener).getName(), partitionerCertRegen()) .step(certRegenJobStep(jobRepository, skipListener)) .gridSize(constants.getNumberOfPartitions()) - .taskExecutor(taskExecutor(constants)) + .taskExecutor(taskExecutor()) .build(); } @@ -1022,7 +1022,7 @@ public Step masterStepEdwSnapshotSchool(JobRepository jobRepository, EducGradBat .partitioner(edwSnapshotSchoolJobStep(jobRepository, skipListener).getName(), partitionerEDWSnapshotSchool()) .step(edwSnapshotSchoolJobStep(jobRepository, skipListener)) .gridSize(constants.getNumberOfPartitions()) - .taskExecutor(taskExecutor(constants)) + .taskExecutor(taskExecutor()) .build(); } @@ -1072,7 +1072,7 @@ public Step masterStepEdwSnapshot(JobRepository jobRepository, EducGradBatchGrad .partitioner(edwSnapshotJobStep(jobRepository, skipListener).getName(), partitionerEDWSnapshot()) .step(edwSnapshotJobStep(jobRepository, skipListener)) .gridSize(constants.getNumberOfPartitions()) - .taskExecutor(taskExecutor(constants)) + .taskExecutor(taskExecutor()) .build(); } @@ -1155,12 +1155,7 @@ public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor() { } @Bean - public TaskExecutor taskExecutor(EducGradBatchGraduationApiConstants constants) { - ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); - executor.setCorePoolSize(constants.getNumberOfPartitions()); - executor.setMaxPoolSize(constants.getNumberOfPartitions()); - executor.setThreadNamePrefix("partition-"); - executor.initialize(); - return executor; + public TaskExecutor taskExecutor() { + return new SimpleAsyncTaskExecutor("partition-"); } } \ No newline at end of file diff --git a/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/config/BatchJobLauncher.java b/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/config/BatchJobLauncher.java index db5ad3fc..52967fcc 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/config/BatchJobLauncher.java +++ b/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/config/BatchJobLauncher.java @@ -46,6 +46,7 @@ public class BatchJobLauncher { private Job userScheduledBatchJobRefresher; @Autowired + @Qualifier("asyncJobLauncher") private JobLauncher jobLauncher; @Autowired diff --git a/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/config/TaskSchedulerConfig.java b/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/config/TaskSchedulerConfig.java index 2c4d823e..123ca079 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/config/TaskSchedulerConfig.java +++ b/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/config/TaskSchedulerConfig.java @@ -36,7 +36,7 @@ public LockableTaskScheduler getScheduler() { LockManager lockManager = new DefaultLockManager(lockProvider, lockConfigurationExtractor); ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); - scheduler.setThreadNamePrefix("UserScheduledTaskScheduler"); + scheduler.setThreadNamePrefix("UserScheduledTask-"); scheduler.initialize(); return new LockableTaskScheduler(scheduler, lockManager); } diff --git a/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/entity/BatchJobExecutionEntity.java b/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/entity/BatchJobExecutionEntity.java index c8d02118..ea51c85a 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/entity/BatchJobExecutionEntity.java +++ b/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/entity/BatchJobExecutionEntity.java @@ -44,8 +44,9 @@ public class BatchJobExecutionEntity { @Column(name = "EXIT_MESSAGE", length = 2500) private String exitMessage; - @Column(name = "LAST_UPDATED") - private Instant lastUpdated; + @Column(name = "LAST_UPDATED", nullable = true) + @Temporal(TemporalType.TIMESTAMP) + private LocalDateTime lastUpdated; @Column(name = "JOB_CONFIGURATION_LOCATION", length = 2500) private String jobConfigurationLocation; diff --git a/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/entity/BatchStepExecutionEntity.java b/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/entity/BatchStepExecutionEntity.java index eefb4f1e..6b01c9e8 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/entity/BatchStepExecutionEntity.java +++ b/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/entity/BatchStepExecutionEntity.java @@ -1,12 +1,9 @@ package ca.bc.gov.educ.api.batchgraduation.entity; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Table; +import jakarta.persistence.*; import lombok.Data; -import java.time.Instant; +import java.time.LocalDateTime; @Data @Entity @@ -27,10 +24,12 @@ public class BatchStepExecutionEntity { private String stepName; @Column(name = "START_TIME") - private Instant startTime = Instant.now(); + @Temporal(TemporalType.TIMESTAMP) + private LocalDateTime startTime = LocalDateTime.now(); @Column(name = "END_TIME") - private Instant endTime; + @Temporal(TemporalType.TIMESTAMP) + private LocalDateTime endTime; @Column(name = "STATUS", length = 10) private String status; @@ -66,9 +65,11 @@ public class BatchStepExecutionEntity { private String exitMessage; @Column(name = "LAST_UPDATED") - private Instant lastUpdated = Instant.now(); + @Temporal(TemporalType.TIMESTAMP) + private LocalDateTime lastUpdated = LocalDateTime.now(); @Column(name = "CREATE_TIME") - private Instant createTime = Instant.now(); + @Temporal(TemporalType.TIMESTAMP) + private LocalDateTime createTime = LocalDateTime.now(); } \ No newline at end of file diff --git a/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/repository/BatchStepExecutionRepository.java b/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/repository/BatchStepExecutionRepository.java new file mode 100644 index 00000000..0d4adcfa --- /dev/null +++ b/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/repository/BatchStepExecutionRepository.java @@ -0,0 +1,14 @@ +package ca.bc.gov.educ.api.batchgraduation.repository; + +import ca.bc.gov.educ.api.batchgraduation.entity.BatchStepExecutionEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface BatchStepExecutionRepository extends JpaRepository { + + List findByJobExecutionIdOrderByEndTimeDesc(Long jobExecutionId); + +} diff --git a/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/service/GradDashboardService.java b/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/service/GradDashboardService.java index 126f1fea..480c3a98 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/service/GradDashboardService.java +++ b/api/src/main/java/ca/bc/gov/educ/api/batchgraduation/service/GradDashboardService.java @@ -2,20 +2,17 @@ import ca.bc.gov.educ.api.batchgraduation.entity.*; import ca.bc.gov.educ.api.batchgraduation.model.*; -import ca.bc.gov.educ.api.batchgraduation.repository.BatchGradAlgorithmJobHistoryRepository; -import ca.bc.gov.educ.api.batchgraduation.repository.BatchGradAlgorithmStudentRepository; -import ca.bc.gov.educ.api.batchgraduation.repository.BatchJobExecutionRepository; -import ca.bc.gov.educ.api.batchgraduation.repository.BatchProcessingRepository; +import ca.bc.gov.educ.api.batchgraduation.repository.*; import ca.bc.gov.educ.api.batchgraduation.rest.RestUtils; import ca.bc.gov.educ.api.batchgraduation.transformer.BatchGradAlgorithmJobHistoryTransformer; import ca.bc.gov.educ.api.batchgraduation.transformer.BatchProcessingTransformer; -import org.apache.commons.lang3.time.DateUtils; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.Duration; import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; @@ -23,10 +20,13 @@ @Service public class GradDashboardService extends GradService { + private static final String JOB_STATUS_STARTED = BatchStatusEnum.STARTED.name(); + private final BatchGradAlgorithmJobHistoryRepository batchGradAlgorithmJobHistoryRepository; private final BatchGradAlgorithmStudentRepository batchGradAlgorithmStudentRepository; private final BatchGradAlgorithmJobHistoryTransformer batchGradAlgorithmJobHistoryTransformer; private final BatchJobExecutionRepository batchJobExecutionRepository; + private final BatchStepExecutionRepository batchStepExecutionRepository; private final BatchProcessingTransformer batchProcessingTransformer; private final BatchProcessingRepository batchProcessingRepository; private final RestUtils restUtils; @@ -34,13 +34,16 @@ public class GradDashboardService extends GradService { public GradDashboardService(BatchGradAlgorithmJobHistoryRepository batchGradAlgorithmJobHistoryRepository, BatchGradAlgorithmJobHistoryTransformer batchGradAlgorithmJobHistoryTransformer, RestUtils restUtils, - BatchJobExecutionRepository batchJobExecutionRepository,BatchProcessingRepository batchProcessingRepository,BatchProcessingTransformer batchProcessingTransformer, + BatchJobExecutionRepository batchJobExecutionRepository, + BatchStepExecutionRepository batchStepExecutionRepository, + BatchProcessingRepository batchProcessingRepository,BatchProcessingTransformer batchProcessingTransformer, BatchGradAlgorithmStudentRepository batchGradAlgorithmStudentRepository) { this.batchGradAlgorithmJobHistoryRepository = batchGradAlgorithmJobHistoryRepository; this.batchGradAlgorithmJobHistoryTransformer = batchGradAlgorithmJobHistoryTransformer; this.batchProcessingTransformer = batchProcessingTransformer; this.batchGradAlgorithmStudentRepository = batchGradAlgorithmStudentRepository; this.batchJobExecutionRepository = batchJobExecutionRepository; + this.batchStepExecutionRepository = batchStepExecutionRepository; this.batchProcessingRepository = batchProcessingRepository; this.restUtils = restUtils; } @@ -50,7 +53,7 @@ public GradDashboard getDashboardInfo() { start(); GradDashboard gradDash = new GradDashboard(); List infoDetailsList= batchGradAlgorithmJobHistoryTransformer.transformToDTO(batchGradAlgorithmJobHistoryRepository.findAll()); - infoDetailsList = infoDetailsList.stream().map(this::handleDeadJob).collect(Collectors.toList()); + infoDetailsList = infoDetailsList.stream().map(this::handleDeadJob).map(this::handleFrozenJob).collect(Collectors.toList()); infoDetailsList.sort(Comparator.comparing(BatchGradAlgorithmJobHistory::getStartTime).reversed()); if(!infoDetailsList.isEmpty()) { BatchGradAlgorithmJobHistory info = infoDetailsList.get(0); @@ -144,30 +147,57 @@ public Optional findBatchProcessing(String jobType) { return batchProcessingRepository.findByJobType(jobType); } + /** + * If any batch jobs have "STARTED" status more than 3 days(72 hours), then treat it as FAILED job + */ @Transactional public BatchGradAlgorithmJobHistory handleDeadJob(BatchGradAlgorithmJobHistory batchJobHistory) { - if ("STARTED".equalsIgnoreCase(batchJobHistory.getStatus()) - && batchJobHistory.getEndTime() == null) { - Integer jobExecutionId = batchJobHistory.getJobExecutionId(); + if (JOB_STATUS_STARTED.equalsIgnoreCase(batchJobHistory.getStatus()) && batchJobHistory.getEndTime() == null) { + LocalDateTime now = LocalDateTime.now(); + Duration duration = Duration.between(batchJobHistory.getStartTime(), now); + long hours = duration.getSeconds() / 3600; + if (hours > 72) { + updateBatchJobStatus(batchJobHistory, BatchStatusEnum.FAILED); + } + } - Date now = new Date(System.currentTimeMillis()); - LocalDateTime deadline = ca.bc.gov.educ.api.batchgraduation.util.DateUtils.toLocalDateTime(DateUtils.addDays(now, -3)); + return batchJobHistory; + } - if (batchJobHistory.getStartTime().isBefore(deadline)) { - Optional optional = batchJobExecutionRepository.findById(jobExecutionId.longValue()); - if (optional.isPresent()) { - BatchJobExecutionEntity batchJobExecution = optional.get(); - if ("UNKNOWN".equalsIgnoreCase(batchJobExecution.getExitCode()) - || BatchStatusEnum.FAILED.toString().equalsIgnoreCase(batchJobExecution.getExitCode()) ) { - BatchGradAlgorithmJobHistoryEntity entity = batchGradAlgorithmJobHistoryTransformer.transformToEntity(batchJobHistory); - entity.setStatus(BatchStatusEnum.FAILED.toString()); - batchGradAlgorithmJobHistoryRepository.save(entity); - batchJobHistory.setStatus(BatchStatusEnum.FAILED.toString()); - } - } + @Transactional + public BatchGradAlgorithmJobHistory handleFrozenJob(BatchGradAlgorithmJobHistory batchJobHistory) { + if (JOB_STATUS_STARTED.equalsIgnoreCase(batchJobHistory.getStatus()) && batchJobHistory.getEndTime() == null) { + Integer jobExecutionId = batchJobHistory.getJobExecutionId(); + if (isFrozen(jobExecutionId.longValue())) { + updateBatchJobStatus(batchJobHistory, BatchStatusEnum.FAILED); } } - return batchJobHistory; } + + /** + * If last updated time is frozen more than 5 hours for all of "STARTED" steps, then treat it as FAILED job + */ + @Transactional(readOnly = true) + public boolean isFrozen(Long jobExecutionId) { + List steps = batchStepExecutionRepository.findByJobExecutionIdOrderByEndTimeDesc(jobExecutionId); + LocalDateTime now = LocalDateTime.now(); + boolean frozenStepFound = false; + for (BatchStepExecutionEntity step : steps) { + if (step.getStepName().contains("partition") && JOB_STATUS_STARTED.equalsIgnoreCase(step.getStatus())) { + Duration duration = Duration.between(step.getLastUpdated(), now); + long hours = duration.getSeconds() / 3600; + frozenStepFound = hours > 5; + } + } + return frozenStepFound; + } + + @Transactional + public void updateBatchJobStatus(BatchGradAlgorithmJobHistory batchJobHistory, BatchStatusEnum batchStatus) { + BatchGradAlgorithmJobHistoryEntity entity = batchGradAlgorithmJobHistoryTransformer.transformToEntity(batchJobHistory); + entity.setStatus(batchStatus.toString()); + batchGradAlgorithmJobHistoryRepository.save(entity); + batchJobHistory.setStatus(BatchStatusEnum.FAILED.toString()); + } } diff --git a/api/src/test/java/ca/bc/gov/educ/api/batchgraduation/service/GradDashboardServiceTest.java b/api/src/test/java/ca/bc/gov/educ/api/batchgraduation/service/GradDashboardServiceTest.java index 980b0ea4..8be796a2 100644 --- a/api/src/test/java/ca/bc/gov/educ/api/batchgraduation/service/GradDashboardServiceTest.java +++ b/api/src/test/java/ca/bc/gov/educ/api/batchgraduation/service/GradDashboardServiceTest.java @@ -1,14 +1,8 @@ package ca.bc.gov.educ.api.batchgraduation.service; -import ca.bc.gov.educ.api.batchgraduation.entity.BatchGradAlgorithmJobHistoryEntity; -import ca.bc.gov.educ.api.batchgraduation.entity.BatchGradAlgorithmStudentEntity; -import ca.bc.gov.educ.api.batchgraduation.entity.BatchJobExecutionEntity; -import ca.bc.gov.educ.api.batchgraduation.entity.BatchProcessingEntity; +import ca.bc.gov.educ.api.batchgraduation.entity.*; import ca.bc.gov.educ.api.batchgraduation.model.*; -import ca.bc.gov.educ.api.batchgraduation.repository.BatchGradAlgorithmJobHistoryRepository; -import ca.bc.gov.educ.api.batchgraduation.repository.BatchGradAlgorithmStudentRepository; -import ca.bc.gov.educ.api.batchgraduation.repository.BatchJobExecutionRepository; -import ca.bc.gov.educ.api.batchgraduation.repository.BatchProcessingRepository; +import ca.bc.gov.educ.api.batchgraduation.repository.*; import ca.bc.gov.educ.api.batchgraduation.rest.RestUtils; import org.apache.commons.lang3.time.DateUtils; import org.junit.Test; @@ -48,6 +42,9 @@ public class GradDashboardServiceTest { @MockBean BatchJobExecutionRepository batchJobExecutionRepository; + @MockBean + BatchStepExecutionRepository batchStepExecutionRepository; + @MockBean BatchProcessingRepository batchProcessingRepository; @@ -86,7 +83,7 @@ public void testGetDashboardInfo_whenStartedDate_isOlderThan3Days_thenUpdateStat hist.setExpectedStudentsProcessed(20L); hist.setJobExecutionId(121L); Date today = new Date(System.currentTimeMillis()); - Date startedDateTime = DateUtils.addDays(today, -3); + Date startedDateTime = DateUtils.addDays(today, -4); hist.setStartTime(ca.bc.gov.educ.api.batchgraduation.util.DateUtils.toLocalDateTime(startedDateTime)); hist.setStatus("STARTED"); list.add(hist); @@ -109,6 +106,48 @@ public void testGetDashboardInfo_whenStartedDate_isOlderThan3Days_thenUpdateStat } + @Test + public void testGetDashboardInfo_whenLastUpdatedDate_isOlderThan5hours_thenUpdateStatusAsFailed() { + + List list = new ArrayList<>(); + BatchGradAlgorithmJobHistoryEntity hist = new BatchGradAlgorithmJobHistoryEntity(); + hist.setId(new UUID(1,1)); + hist.setExpectedStudentsProcessed(20L); + hist.setJobExecutionId(121L); + Date today = new Date(System.currentTimeMillis()); + Date startedDateTime = DateUtils.addDays(today, -1); + hist.setStartTime(ca.bc.gov.educ.api.batchgraduation.util.DateUtils.toLocalDateTime(startedDateTime)); + hist.setStatus("STARTED"); + list.add(hist); + + BatchJobExecutionEntity batchJobExecution = new BatchJobExecutionEntity(); + batchJobExecution.setJobExecutionId(hist.getJobExecutionId()); + batchJobExecution.setId(Long.valueOf("123")); + batchJobExecution.setStatus("STARTED"); + batchJobExecution.setStartTime(hist.getStartTime()); + + BatchStepExecutionEntity step = new BatchStepExecutionEntity(); + step.setStepName("test-partition12"); + step.setJobExecutionId(hist.getJobExecutionId()); + step.setId(Long.valueOf("123")); + step.setStatus("STARTED"); + step.setStartTime(ca.bc.gov.educ.api.batchgraduation.util.DateUtils.toLocalDateTime(startedDateTime)); + Date lastUpdatedDateTime = DateUtils.addHours(today, -6); + step.setLastUpdated(ca.bc.gov.educ.api.batchgraduation.util.DateUtils.toLocalDateTime(lastUpdatedDateTime)); + batchJobExecution.setStartTime(hist.getStartTime()); + + when(batchGradAlgorithmJobHistoryRepository.findAll()).thenReturn(list); + when(batchJobExecutionRepository.findById(hist.getJobExecutionId())).thenReturn(Optional.of(batchJobExecution)); + when(batchStepExecutionRepository.findByJobExecutionIdOrderByEndTimeDesc(hist.getJobExecutionId())).thenReturn(List.of(step)); + + GradDashboard dash = gradDashboardService.getDashboardInfo(); + assertThat(dash).isNotNull(); + assertThat(dash.getTotalBatchRuns()).isEqualTo(1); + assertThat(dash.getBatchInfoList()).isNotEmpty(); + assertThat(dash.getBatchInfoList().get(0).getStatus()).isEqualTo("FAILED"); + + } + @Test public void testgetProcessingList() {