데이터 추출을 위한 조건을 통한 조회 시 아래와 같은 문제가 발생하였다.
(chunkSize,PageSize는 모두 1로 가정)
문제
MailHistory테이블의 flg값이 X인 애들을 모두 O로 변경하는 배치 작업을 구현하였으나 띄엄띄엄 업데이트가 되는 상황을 마주하였다.
기존 코드
MailHistory엔티티
@Entity
public class MailHistoryEntity extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long historyNo;
...
private String flg;
}
이메일 재발송 배치 Config
@Slf4j
@Configuration
@RequiredArgsConstructor
public class EmailRetryBatchConfig {
private final EntityManagerFactory entityManagerFactory;
private final MailService mailService;
private int chunkSize=1;
@Bean
public Job emailRetryJob(PlatformTransactionManager transactionManager, JobRepository jobRepository){
return new JobBuilder("emailRetryJob",jobRepository)
.start(emailRetryStep(transactionManager,jobRepository))
.build()
;
}
public Step emailRetryStep(PlatformTransactionManager transactionManager, JobRepository jobRepository){
return new StepBuilder("emailRetryStep",jobRepository)
.<MailHistoryEntity,MailHistoryEntity>chunk(chunkSize,transactionManager)
.reader(emailRetryReader(null))
.processor(emailRetryProcessor())
.writer(emailRetryWriter())
.build()
;
}
@Bean
@StepScope
public JpaPagingItemReader<MailHistoryEntity> emailRetryReader(@Value("#{jobParameters[nowDt]}") String nowDt){
log.info("emailRetryReader start");
JpaPagingItemReader<MailHistoryEntity> reader = new JpaPagingItemReader<>();
reader.setPageSize(chunkSize);
reader.setParameterValues(parameterMap);
reader.setName("emailRetryReader");
reader.setEntityManagerFactory(entityManagerFactory);
reader.setQueryString("select m from MailHistoryEntity m where flg='X' order by historyNo");
return reader;
}
@Bean
public ItemProcessor<MailHistoryEntity,MailHistoryEntity> emailRetryProcessor(){
log.info("emailRetryProcessor start");
return item -> {
log.info("이메일 재발송 처리");
item.sendSuccess();
return item;
};
}
@Bean
public ItemWriter<MailHistoryEntity> emailRetryWriter(){
log.info("emailRetryWriter start");
JpaItemWriter<MailHistoryEntity> writer = new JpaItemWriter<>();
writer.setEntityManagerFactory(entityManagerFactory);
}
}
문제 발생 원인
조회 조건 존재 시 Paging의 대한 문제이다.
페이지 조회 쿼리
페이징 별 데이터 값을 살펴보자.
첫 페이징) limit 0,1
첫 페이징 조회 시 조회 조건에 해당하는 데이터들 중 limit 0,1 조회 시 HistoryNo 393에 해당하는 로우데이터가 조회된다.
그리고 해당 데이터는 업데이트가 된다.
두번째 페이징) limit 1,1
두번째 페이징에선 조회 조건에 해당하는 데이터들 중 limit 1,1 조회 시 396 데이터가 조회되어 해당 데이터 업데이트한다.
의도대로라면 393~397 내에서 조회되어 페이징 처리되어야 하나 조회 조건으로 인해 394~397 내에서 조회되어 처리된다.
해결
1.Cursor
Jpa는 지원되지 않아 따로 실제 구현은 하지 않음
2. PagingReader Override
첫번째,두번째, 그 이후 페이지 모두 대상 데이터 조회 시 이미 업데이트된 데이터 제외하고 조회되므로 시작값을 0으로 둘 수 있도록 한다. (limit 0,1 으로 고정)
@Bean
@StepScope
public JpaPagingItemReader<MailHistoryEntity> emailRetryReader(@Value("#{jobParameters[nowDt]}") String nowDt){
log.info("emailRetryReader start");
//override 통한 문제 해결
JpaPagingItemReader<MailHistoryEntity> reader = new JpaPagingItemReader<MailHistoryEntity>() {
@Override
public int getPage() {
return 0;
}
};
reader.setPageSize(chunkSize);
reader.setParameterValues(parameterMap);
reader.setName("emailRetryReader");
reader.setEntityManagerFactory(entityManagerFactory);
reader.setQueryString("select m from MailHistoryEntity m where createdDt=:nowDt and flg='X' order by historyNo");
return reader;
}
위와 같이 수정 후 배치 수행 시 모두 정상적으로 업데이트 성공하였다.
참고
https://jojoldu.tistory.com/337
Spring Batch Paging Reader 사용시 같은 조건의 데이터를 읽고 수정할때 문제
안녕하세요. 이번 시간에는 Spring Batch를 사용하시는 분들이 자주 묻는 질문 중 하나인 같은 조건의 데이터를 읽고 수정할때 어떻게 해야하는지 에 대해서 소개드리려고 합니다. 모든 코드는 Githu
jojoldu.tistory.com
'SpringBoot > 오류' 카테고리의 다른 글
SpringSecurity 순환 참조(circular references) 발생 (1) | 2024.09.11 |
---|---|
@Valid MethodArgumentNotValidException 처리 (0) | 2024.08.23 |
@OneToOne 지연로딩? (0) | 2024.04.22 |
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: Library.Library.Member.Member.heartList: could not initialize proxy - no Session 에러 (0) | 2023.08.29 |
@EmbeddedId @GeneratedValue 복합키 키자동증가 불가 (0) | 2023.05.16 |