프로젝트/스프링 배치 튜토리얼
Spring Batch Skip Policy
Junuuu
2023. 10. 12. 00:01
반응형
개요
Spring Batch를 실행하다가 예외가 발생하면 어떻게 되는지 알아보고, 안정적인 Batch Application을 만들기 위해서 어떤 방안이 있는지 알아보고자 합니다.
예외가 간헐적으로 발생하는 Batch Application 구성
@Profile("skip-job")
@Configuration
class SkipJobConfig(
private val jobRepository: JobRepository,
private val batchTransactionManager: PlatformTransactionManager,
) {
@Bean
fun skipJob(): Job {
return JobBuilder("skipJob job", jobRepository)
.incrementer(RunIdIncrementer())
.start(chunkStep())
.build()
}
@Bean
fun chunkStep(): Step {
return StepBuilder("skipJob step", jobRepository)
.chunk<String, String>(BATCH_SIZE, batchTransactionManager)
.reader(beanReader())
.writer(ClassItemWriter())
.build()
}
@Bean
fun beanReader(): ItemReader<String> {
val data: List<String> = mutableListOf(
"Byte",
"Code",
"Data",
"Disk",
"File",
"Input",
"Loop",
"Logic",
"Mode",
"Node",
"Port",
"Query",
"Ratio",
"Root",
"Route",
"Scope",
"Syntax",
"Token",
"Trace"
)
logger.info ("Reading item: {}", data)
return ListItemReader(data)
}
class ClassItemWriter: ItemWriter<String> {
override fun write(itmes: Chunk<out String>) {
logger.info("Writing item start")
itmes.forEach {
logger.info("Writing item : {}", it)
if(it.contains('o')){
throw IllegalArgumentException("문자에 o가 들어가는 경우 예외를 만들면 어떻게 될까?")
}
}
}
}
companion object {
private const val BATCH_SIZE = 5
}
}
예외와 함께 batch_job_execution table에 실패로 기록됩니다.
특정 예외가 발생했을 때 Skip 처리
@Bean
fun chunkStep(): Step {
return StepBuilder("skipJob step", jobRepository)
.chunk<String, String>(BATCH_SIZE, batchTransactionManager)
.reader(beanReader())
.writer(ClassItemWriter())
.faultTolerant()
.skip(IllegalArgumentException::class.java)
.skipLimit(20)
.build()
}
오류가 발생할 경우 장애를 처리하기 위한 기능을 제공하는 faultTolerant()를 선언합니다.
그리고 어떤 예외에서 skip을 수행할지 몇 번까지 허용할지에 대한 정책을 결정합니다.
위의 코드에서는 IllegalArgumentException에 대한 예외는 Skip 하고 최대 20번까지 허용합니다.
이제 더이상 오류가 발생해도 Step이 즉시 종료되지 않고 20번까지는 예외를 허용합니다.
작업을 수행하고 batch_step_execution 테이블의 데이터를 보면 11번의 write_skip_count를 가지며 작업은 성공적으로 끝났음을 알 수 있습니다.
예외를 핸들링하는 SkipPolicy 구현
@Configuration
class CustomSkipPolicy : SkipPolicy {
companion object {
private const val MAX_SKIP_COUNT = 10
}
override fun shouldSkip(throwable: Throwable, skipCount: Long): Boolean {
if (throwable is IllegalArgumentException && skipCount < MAX_SKIP_COUNT) {
return true
}
return false
}
}
@Bean
fun chunkStep(): Step {
return StepBuilder("skipJob step", jobRepository)
.chunk<String, String>(BATCH_SIZE, batchTransactionManager)
.reader(beanReader())
.writer(ClassItemWriter())
.faultTolerant()
// .skip(IllegalArgumentException::class.java)
// .skipLimit(20)
.skipPolicy(CustomSkipPolicy())
.build()
}
조금 더 정교한 SkipPolicy를 위해서 Spring Batch에서는 SkipPolicy 인터페이스를 제공합니다.
Skip 조건을 직접 구현할 수 있습니다.
이번에는 10번 허용하도록 해보겠습니다.
위에서는 11번의 Skip이 발생했으므로 CustomSkipPolicy를 적용하게 되면 Step은 실패해야 합니다.
예상대로 실패하게되고 SKIP_COUNT를 20으로 늘리면 다시 성공합니다.
참고자료
https://www.baeldung.com/spring-batch-skip-logic