SpringBatch 에서 StepExecution 알아보기
- Coding/Spring Batch
- 2022. 1. 16.
들어가기전
지난 포스팅에서 JobExecution에 대해 알아보았다. 이를 먼저 인지한 후 StepExecution에 대해 알아보자.
https://devfunny.tistory.com/680?category=820618
예제코드
- StepExecutionConfiguration.java
package com.spring.batch.step;
import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@RequiredArgsConstructor
public class StepExecutionConfiguration {
// job 생성
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job stepExecutionTestJob() {
return this.jobBuilderFactory.get("stepExecutionTestJob")
.start(executionTestStep1())
.next(executionTestStep2())
.next(executionTestStep3())
.build();
}
@Bean
public Step executionTestStep1() {
return stepBuilderFactory.get("executionTestStep1")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("executionTestStep1");
return RepeatStatus.FINISHED;
}
})
.build();
}
@Bean
public Step executionTestStep2() {
return stepBuilderFactory.get("executionTestStep2")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("executionTestStep1");
throw new RuntimeException("step2 failed");
// return RepeatStatus.FINISHED;
}
})
.build();
}
@Bean
public Step executionTestStep3() {
return stepBuilderFactory.get("executionTestStep3")
// 빈으로 등록해도되고, 이렇게 객체 생성해도 된다.
.tasklet(new CustomTasklet())
.build();
}
}
- CustomTasklet.java
package com.spring.batch.step;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
public class CustomTasklet implements Tasklet {
/**
* 비즈니스 로직 구현
* @param stepContribution
* @param chunkContext
* @return
* @throws Exception
*/
@Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
System.out.println("taskletTestStep2");
return RepeatStatus.FINISHED;
}
}
1) 생성될 StepExecution 개수
...
@Bean
public Job stepExecutionTestJob() {
return this.jobBuilderFactory.get("stepExecutionTestJob")
/**
* StepExecution 은 총 3개 생성되겠다. (Step이 3개이므로)
* 각 스텝별 상태를 저장한다. (BATCH_STEP_EXECUTION)
* 3개의 StepExecution 의 부모 JobExecution 은 현재의 jobExecution 1개로 동일하다.
*/
.start(executionTestStep1())
.next(executionTestStep2())
.next(executionTestStep3())
.build();
}
...
stepExecutionTestJob은 총 Step1, Step2, Step3 으로 구성되어있다. 각 Step 별로 새로운 StepExecution 객체가 생성되므로 3개의 row 가 BATCH_STEP_EXECUTION 에 insert 될거라고 예상한다.
(예상한 데이터)
BATCH_STEP_EXECUTION | exec |
step1 - stepExecution | 1 row insert |
step2 - stepExecution | 1 row insert |
step3 - stepExecution | 1 row insert |
2) stpe1은 정상 종료 후, step2 에서 에러가 발생했다.
...
@Bean
public Step executionTestStep2() {
return stepBuilderFactory.get("executionTestStep2")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("executionTestStep1");
throw new RuntimeException("step2 failed");
// return RepeatStatus.FINISHED;
}
})
.build();
}
...
step2 가 에러가 발생한 후, 해당 Job은 종료되었다. Job의 상태는 FAILED이다.
(등록된 데이터) : step2에서 에러가 발생했으므로 예상했던 데이터가 아니다.
BATCH_STEP_EXECUTION | exec |
step1 - stepExecution | 1 row insert (COMPLETED) |
step2 - stepExecution | 1 row insert (FAILED) |
여기서 알수있는 것은, 실패한 step2 에 대한 StepExecution은 insert 되었다는 점이다. StepExecution 은 Step이 실행되는 초기에 생성되기 때문에 실패 여부와 상관없이 insert 된다. 대신, 해당 stepExecution의 상태값은 FAILEd 이다. Step2에 대한 오류 정보는 EXIT_CODE, EXIT_MESSAGE 를 통해 알아낼 수 있다.
3) step2의 고의 에러 발생 로직을 수정하고, 모든 step이 성공하는 잡을 재실행해보자.
package com.spring.batch.step;
import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@RequiredArgsConstructor
public class StepExecutionConfiguration {
// job 생성
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job stepExecutionTestJob() {
return this.jobBuilderFactory.get("stepExecutionTestJob")
.start(executionTestStep1())
.next(executionTestStep2())
.next(executionTestStep3())
.build();
}
@Bean
public Step executionTestStep1() {
return stepBuilderFactory.get("executionTestStep1")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("executionTestStep1");
return RepeatStatus.FINISHED;
}
})
.build();
}
@Bean
public Step executionTestStep2() {
return stepBuilderFactory.get("executionTestStep2")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("executionTestStep1");
throw new RuntimeException("step2 failed");
// return RepeatStatus.FINISHED;
}
})
.build();
}
@Bean
public Step executionTestStep3() {
return stepBuilderFactory.get("executionTestStep3")
// 빈으로 등록해도되고, 이렇게 객체 생성해도 된다.
.tasklet(new CustomTasklet())
.build();
}
}
(데이터 조회)
BATCH_STEP_EXECUTION | exec |
이전에 이미 성공했던 Step 이므로 실행하지 않는다. | |
step2 - stepExecution | 1 row insert |
step3 - stepExecution | 1 row insert |
동일한 Job 실행시, 이전에 이미 Step이 성공됐었다면(COMPLETED) 해당 Step 은 실행되지 않는다. 따라서 그 이후의 step2, step3에 대한 StepExecution만 생성된 결과를 볼 수 있다.
동일한 Job 실행시, 성공 여부와 관련없이 모든 Step을 실행시킬 수 있는 방법도 있다. 이는 다음 포스팅에서 알아보자.
'Coding > Spring Batch' 카테고리의 다른 글
SpringBatch에서 JobLauncher 동기/비동기 실행 (0) | 2022.01.21 |
---|---|
스프링배치 StepContribution (0) | 2022.01.19 |
SpringBatch 에서 JobInstance, JobExecution 의 관계 (0) | 2022.01.14 |
[스프링 배치] Spring Batch + mysql 설정하기 (0) | 2021.10.09 |
[스프링 배치] 두가지 포맷의 파일을 각 포맷(접두어)에 따라 처리하기 (PatternMatchingCompositeLineMapper , LineTokenizer, FieldSetMapper (0) | 2021.10.08 |