ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 스프링 배치 도메인 이해 -StepExecution
    Study/Spring Batch 2024. 2. 15. 07:49

    StepExecution

    1) 기본 개념

    • Step에 대한 한 번의 시도를 의미하는 객체로 Step 실행 중에 발생한 정보들을 저장하고 있는 객체.
      • 시작시간, 종료시간, 상태(시작됨/완료/실패), commit count, rollback count 등의 속성을 가진다.
    • Step이 매번 시도될 때마다 각 Step별로 생성된다.
    • Job이 재시작하더라도 이미 성공적으로 완료된 Step은 재실행되지않고 실패한 Step만 실행된다.
    • 이전 단계의 Step이 실패해서 현재 Step을 실행하지 않았다면 StepExecution을 생성하지 않는다. Step이 실제로 시작되었을 때만 StepExecution을 생성한다.
    • JobExecution과의 관계
      • Step의 StepExecution이 모두 정상적으로 완료되어야 JobExecution이 정상적으로 완료된다.
      • Step의 StepExecution중 하나라도 실패하면 JobExecution은 실패한다.

     

    2) BATCH_STEP_EXECUTION 테이블과 매핑

    • JobExecution과 StepExecution은 1:N의 관계
    • 하나의 Job을 여러 개의 Step으로 구성했을 경우 각 StepExecution은 하나의 JobExecution을 부모로 가진다.

     

    2-1) Step1, Step2 모두 성공한 케이스

    BATCH_JOB_EXECUTION

    JobExecution ID JobInstance ID Start Time End Time Status
    1 1 2021.01.01.12:00 2021.01.01.12:10 COMPLETED

    BATCH_STEP_EXECUTION

    StepExecution ID Step Name JobExecution ID Status
    1 Step1 1 COMPLETED
    2 Step2 1 COMPLETED

     

    2-2) Step1 성공, Step2 실패한 케이스

    BATCH_JOB_EXECUTION

    JobExecution ID JobInstance ID Start Time End Time Status
    2 2 2021.01.02.12:00 2021.01.02.12:10 FAILED

    BATCH_STEP_EXECUTION

    StepExecution ID Step Name JobExecution ID Status
    3 Step1 2 COMPLETED
    4 Step2 2 FAILED

     

    2-3) JobInstanceA 성공, JobInstanceB 실패, JobInstanceB 재시도 성공 케이스 도식화

     

    3) StepExecution 속성

     

    실습

    실습 1) step1, step2, step3 모두 성공

    더보기

    StepConfiguration.java 전체 소스코드

    @RequiredArgsConstructor
    @Configuration
    public class StepConfiguration {
    
        private final JobBuilderFactory jobBuilderFactory;
        private final StepBuilderFactory stepBuilderFactory;
    
        @Bean
        public Job BatchJob() {
            return this.jobBuilderFactory.get("Job")
                    .start(step1())
                    .next(step2())
                    .next(step3())
                    .build();
        }
    
        @Bean
        public Step step1() {
            return stepBuilderFactory.get("step1")
                    .tasklet(new Tasklet() { // 편의상 익명클래스를 사용했지만 클래스로 만들어도 된다. step3 참고
                        // StepBuilder.tasklet -> TaskletStepBuilder을 반환한다.
                        // TaskletStepBuilder : Tasklet을 만들어주는 빌더클래스.
                        @Override
                        public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
                            System.out.println("step1 has executed");
                            return RepeatStatus.FINISHED;
                        }
                    })
                    .build(); // AbstractTaskletStepBuilder.build -> TaskletStep을 반환한다.
        }
        @Bean
        public Step step2() { // 람다로 구성
            return stepBuilderFactory.get("step2")
                    .tasklet((contribution, chunkContext) -> {
                        System.out.println("step2 has executed");
                        return RepeatStatus.FINISHED;
                    })
                    .build();
        }
    
        @Bean
        public Step step3() {
            return stepBuilderFactory.get("step3")
                    .tasklet(new CustomTasklet())
                    .build();
        }
    }

    - BATCH_JOB_EXECUTION 테이블

    SELECT JOB_EXECUTION_ID, JOB_INSTANCE_ID, STATUS, EXIT_CODE, EXIT_MESSAGE
    FROM BATCH_JOB_EXECUTION
    WHERE JOB_EXECUTION_ID = 13

    - BATCH_STEP_EXECUTION 테이블

    SELECT STEP_EXECUTION_ID, STEP_NAME, JOB_EXECUTION_ID, STATUS, COMMIT_COUNT, ROLLBACK_COUNT, EXIT_CODE, EXIT_MESSAGE
    FROM BATCH_STEP_EXECUTION
    WHERE JOB_EXECUTION_ID = 13

     

    실습 2) step2에서 에러 발생

        @Bean
        public Step step2() { // 람다로 구성
            return stepBuilderFactory.get("step2")
                    .tasklet((contribution, chunkContext) -> {
                        System.out.println("step2 has executed");
                        throw new RuntimeException("step2 has failed");
    //                    return RepeatStatus.FINISHED;
                    })
                    .build();
        }
    2024-02-15 07:06:29.343  INFO 11781 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=Job]] launched with the following parameters: [{name=user3}]
    2024-02-15 07:06:29.378  INFO 11781 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
    step1 has executed
    2024-02-15 07:06:29.403  INFO 11781 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [step1] executed in 25ms
    2024-02-15 07:06:29.427  INFO 11781 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step2]
    step2 has executed
    2024-02-15 07:06:29.439 ERROR 11781 --- [           main] o.s.batch.core.step.AbstractStep         : Encountered an error executing step step2 in job Job
    
    java.lang.RuntimeException: step2 has failed
    	at io.springbatch.springbatchlecture.StepConfiguration.lambda$step2$0(StepConfiguration.java:50) ~[classes/:na]

    - BATCH_JOB_EXECUTION 테이블

    SELECT JOB_EXECUTION_ID, JOB_INSTANCE_ID, STATUS, EXIT_CODE, EXIT_MESSAGE
    FROM BATCH_JOB_EXECUTION
    WHERE JOB_EXECUTION_ID = 14

    - BATCH_STEP_EXECUTION 테이블

    SELECT STEP_EXECUTION_ID, STEP_NAME, JOB_EXECUTION_ID, STATUS, COMMIT_COUNT, ROLLBACK_COUNT, EXIT_CODE, EXIT_MESSAGE
    FROM BATCH_STEP_EXECUTION
    WHERE JOB_EXECUTION_ID = 14

    step1 COMPLETED, step2가 FAILED 상태로 남고 step3에 대한 step execution은 생성되지 않았다.

     

    실습 3) 실습2 재시도

    - BATCH_JOB_EXECUTION 테이블

    SELECT JOB_EXECUTION_ID, JOB_INSTANCE_ID, STATUS, EXIT_CODE, EXIT_MESSAGE
    FROM BATCH_JOB_EXECUTION
    WHERE JOB_EXECUTION_ID = 15

    - BATCH_STEP_EXECUTION 테이블

    SELECT STEP_EXECUTION_ID, STEP_NAME, JOB_EXECUTION_ID, STATUS, COMMIT_COUNT, ROLLBACK_COUNT, EXIT_CODE, EXIT_MESSAGE
    FROM BATCH_STEP_EXECUTION
    WHERE JOB_EXECUTION_ID = 15

    step2만 FAILED로 남고 실행되지 않은 step1(이미 성공), step3(이전에 실패)에 대한 step execution은 생성되지 않았다.

     

    실습 4) 실습3 성공으로 변경하여 재시도

        @Bean
        public Step step2() { // 람다로 구성
            return stepBuilderFactory.get("step2")
                    .tasklet((contribution, chunkContext) -> {
                        System.out.println("step2 has executed");
    //                    throw new RuntimeException("step2 has failed");
                        return RepeatStatus.FINISHED;
                    })
                    .build();
        }

    - BATCH_JOB_EXECUTION 테이블

    SELECT JOB_EXECUTION_ID, JOB_INSTANCE_ID, STATUS, EXIT_CODE, EXIT_MESSAGE
    FROM BATCH_JOB_EXECUTION
    WHERE JOB_EXECUTION_ID = 16

    - BATCH_STEP_EXECUTION 테이블

    SELECT STEP_EXECUTION_ID, STEP_NAME, JOB_EXECUTION_ID, STATUS, COMMIT_COUNT, ROLLBACK_COUNT, EXIT_CODE, EXIT_MESSAGE
    FROM BATCH_STEP_EXECUTION
    WHERE JOB_EXECUTION_ID = 16

    step2, step3이 COMPLETED로 남고 이미 성공하여 실행되지 않은 step1의 step execution은 생성되지 않았다.

     

    댓글

Designed by Tistory.