Skip to content

Commit

Permalink
feat: 배치 작업 실패 시 retry, skip 설정
Browse files Browse the repository at this point in the history
  • Loading branch information
dgjinsu committed May 3, 2024
1 parent f4ced8b commit 03a6b9d
Showing 1 changed file with 61 additions and 24 deletions.
85 changes: 61 additions & 24 deletions src/main/java/jikgong/global/batch/job/ApplyJobConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import jikgong.domain.notification.entity.NotificationType;
import jikgong.domain.notification.service.NotificationService;
import jikgong.domain.workDate.entity.WorkDate;
import jikgong.global.exception.CustomException;
import jikgong.global.exception.ErrorCode;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
Expand All @@ -22,6 +24,9 @@
import org.springframework.batch.item.database.builder.JpaItemWriterBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.backoff.FixedBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.transaction.PlatformTransactionManager;

import java.time.LocalDate;
Expand All @@ -35,6 +40,8 @@ public class ApplyJobConfig {
private final NotificationService notificationService;
private final EntityManagerFactory entityManagerFactory;

private static final int CHUNK_SIZE = 3;

@Bean
public Job applyProcessJob(JobRepository jobRepository, Step applyProcessStep) {
return new JobBuilder("applyProcessJob", jobRepository)
Expand All @@ -45,16 +52,19 @@ public Job applyProcessJob(JobRepository jobRepository, Step applyProcessStep) {
@Bean
public Step applyProcessStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
return new StepBuilder("applyProcessStep", jobRepository)
.<Apply, Apply>chunk(100, transactionManager)
.<Apply, Apply>chunk(CHUNK_SIZE, transactionManager)
.reader(applyReader())
.processor(applyProcessor())
.writer(applyWriter())
.faultTolerant()
.retryPolicy(retryPolicy())
.skipPolicy(new CustomSkipPolicy()) // Custom Skip 정책 설정
.build();
}

@Bean
public ItemReader<Apply> applyReader() {
LocalDate tomorrow = LocalDate.now().plusDays(1);
LocalDate now = LocalDate.now();
JpaPagingItemReader<Apply> reader = new JpaPagingItemReader<>() {
@Override
public int getPage() {
Expand All @@ -64,38 +74,41 @@ public int getPage() {
reader.setName("applyReader");
reader.setPageSize(100);
reader.setEntityManagerFactory(entityManagerFactory);
reader.setQueryString("select a from Apply a join fetch a.workDate w join fetch a.member m join fetch a.workDate.jobPost j where w.workDate <= :tomorrow and a.status = 'PENDING'");
reader.setQueryString("select a from Apply a join fetch a.workDate w join fetch a.member m join fetch a.workDate.jobPost j where w.date < :now and a.status = 'PENDING'");
Map<String, Object> parameters = new HashMap<>();
parameters.put("tomorrow", tomorrow);
parameters.put("now", now);
reader.setParameterValues(parameters);
return reader;
}
// return new RepositoryItemReaderBuilder<Apply>()
// .name("applyReader")
// .repository(applyRepository)
// .methodName("findNeedToCancel")
// .pageSize(1)
// .arguments(List.of(tomorrow))
// .sorts(Collections.singletonMap("id", Sort.Direction.ASC)) // 필수
// .build();
// }

@Bean
public ItemProcessor<Apply, Apply> applyProcessor() {
return apply -> {
Member member = apply.getMember();
WorkDate workDate = apply.getWorkDate();
JobPost jobPost = workDate.getJobPost();
// 재시도 정책
RetryTemplate retryTemplate = retryTemplate();

return retryTemplate.execute(context -> {
if (context.getRetryCount() > 0) {
log.warn("배치 작업 재시도. apply id: {}, 시도 횟수: {}", apply.getId(), context.getRetryCount());
}
Member member = apply.getMember();
WorkDate workDate = apply.getWorkDate();
JobPost jobPost = workDate.getJobPost();

// 알림 전송
String content = workDate.getDate().toString() + ", " + jobPost.getTitle() + " 현장 지원이 자동 취소되었습니다.";
String url = "/api/worker/job-post/" + jobPost.getId();
notificationService.saveNotification(member.getId(), NotificationType.APPLY_CANCEL, content, url);
// 알림 전송
String content = workDate.getDate().toString() + ", " + jobPost.getTitle() + " 현장 지원이 자동 취소되었습니다.";
String url = "/api/worker/job-post/" + jobPost.getId();
notificationService.saveNotification(member.getId(), NotificationType.APPLY_CANCEL, content, url);

// apply status 업데이트
apply.updateStatus(ApplyStatus.CANCELED, LocalDateTime.now());
// apply status 업데이트
apply.updateStatus(ApplyStatus.CANCELED, LocalDateTime.now());

return apply;
return apply;
}, context -> {
// 재시도 실패 후 처리
log.error("모든 재시도 실패. 실패한 Apply ID: {}", apply.getId());
return null;
});
};
}

Expand All @@ -105,4 +118,28 @@ public ItemWriter<Apply> applyWriter() {
.entityManagerFactory(entityManagerFactory)
.build();
}
}

// RetryTemplate 빈 생성 (재시도 정책 설정)
@Bean
public RetryTemplate retryTemplate() {
RetryTemplate retryTemplate = new RetryTemplate();

FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(5000); // 재시도 간 5초 대기
retryTemplate.setBackOffPolicy(fixedBackOffPolicy);

SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(3); // 최대 재시도 횟수 3회
retryTemplate.setRetryPolicy(retryPolicy);

return retryTemplate;
}

// SimpleRetryPolicy 빈 생성 (최대 재시도 횟수 3회)
@Bean
public SimpleRetryPolicy retryPolicy() {
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(3);
return retryPolicy;
}
}

0 comments on commit 03a6b9d

Please sign in to comment.