SpringBatch 에서 StepExecution 알아보기

반응형
728x90
반응형

들어가기전

지난 포스팅에서 JobExecution에 대해 알아보았다. 이를 먼저 인지한 후 StepExecution에 대해 알아보자.

https://devfunny.tistory.com/680?category=820618 

 

SpringBatch 에서 JobInstance, JobExecution 의 관계

들어가기전 JobInstance, JobExecution 의 개념은 알고가자. https://devfunny.tistory.com/476?category=820618 [스프링배치] 잡의 실행 (JobLauncher, JobInstance, JobExecution, JobParameters) 잡의 실행 잡..

devfunny.tistory.com

 

 

 

예제코드

  • 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)
step3 - stepExecution  

 

여기서 알수있는 것은, 실패한 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
step1 - stepExecution 이전에 이미 성공했던 Step 이므로 실행하지 않는다.
step2 - stepExecution 1 row insert
step3 - stepExecution 1 row insert

 

동일한 Job 실행시, 이전에 이미 Step이 성공됐었다면(COMPLETED) 해당 Step 은 실행되지 않는다. 따라서 그 이후의 step2, step3에 대한 StepExecution만 생성된 결과를 볼 수 있다.

 

동일한 Job 실행시, 성공 여부와 관련없이 모든 Step을 실행시킬 수 있는 방법도 있다. 이는 다음 포스팅에서 알아보자.

 

 

반응형

Designed by JB FACTORY