티스토리 뷰

반응형

스프링 배치를 개발하다보면 메타데이터 테이블을 다루게 되고

그중에 한 예로 BATCH_JOB_EXECUTION 테이블은 아래와 같이 구성되어 있습니다.

https://docs.spring.io/spring-batch/docs/3.0.x/reference/html/metaDataSchema.html

필드 중에 STATUS, EXIT_CODE 두개의 필드가 존재하는데요.

(BATCH_STEP_EXECUTION 테이블에도 동일하게 두개의 필드가 존재합니다)

 

STATUS와 EXIT_CODE가 배치 실행시 사용되는 값에 어떤 차이가 있는지에 대해 궁금증이 생겼습니다.

 

그래서 이번에는 STATUS, EXIT_CODE 두개의 필드에 대해 공부해보겠습니다.

 

 

STATUS 란?


STATUS는 배치의 실행 상태를 나타내는 값입니다.

주로 배치가 실행 중인지, 완료되었는지를 나타냅니다.

 

스프링 배치에서는 BatchStatus라는 열거형 클래스를 통해 값을 다루고 있는데요.

BatchStatus가 다루는 값의 종류는 아래와 같습니다.

  • ABANDONED: 제대로 중지되지 않아 다시 시작할 수 없는 배치 작업의 상태
  • COMPLETED: 배치 작업 실행을 성공적으로 완료
  • FAILED: 배치 작업 실행 중에 실패
  • STARTED: 배치 작업이 실행중
  • STARTING: 배치 작업 실행 전 상태
  • STOPPED: 요청에 의해 중지된 배치 작업
  • STOPPING: 배치 작업을 중지하기 전에 단계가 완료되기를 기다리는 배치 작업 상태
  • UNKNOWN: 불확실한 상태인 배치 작업의 상태

 

 

EXIT_CODE 란?


EXIT_CODE는 배치 처리 작업에 대한 상태를 나타내는 값입니다.

STATUS는 배치의 실행여부에 초점을 맞춘다면 EXIT_CODE는 배치처리에 대해 나타내줍니다.

 

스프링 배치에서는 ExitStatus라는 클래스를 통해 값을 다루고 있는데요.

ExitStatus 값의 종류로는 아래와 같습니다.

  • COMPLETED: 배치 실행이 정상적으로 종료되었을때
  • EXECUTING: 배치 처리가 진행중
  • FAILED: 배치 처리가 실패함
  • NOOP: 배치 작업을 처리할 게 없는경우 ex) 배치가 이미 완료가 된경우
  • STOPPED: 배치 처리 작업 중간에 중단된 상태
  • UNKNOWN: 알 수 없는 상태, 배치 재시작이 불가능하다

 

배치 처리시 상태값 변화


STATUS, EXIT_CODE 필드에 대해 간단한 정의들을 보았는데 이것만으로는 정확한 이해가 어렵기 때문에 

실제로 배치가 동작하는 동안 각각의 상태값이 어떻게 변하는지를 살펴보겠습니다.

 

지금부터는 코드를 기준으로 상태값 변화를 살펴볼거기 때문에 STATUS는 BatchStatus, EXIT_CODE는 ExitStatus 용어로 사용하도록 하겠습니다.

 

아래는 간단한 배치가 실행될때 배치 상태값의 변화를 정리해본 그림입니다.

  1. Job 실행 전 배치 실행을 준비하는 단계로 배치작업 실행 전을 의미하는 STARTING 상태값을 설정합니다.
    BatchStatus: STARTING, ExitStatus: UNKNOWN
  2. Job 실행 한 직후, 배치가 실행되었으므로 STARTING -> STARTED 값으로 업데이트 합니다.
  3. Step 실행 한 직후, 배치 처리가 진행되므로 ExitStatus: EXECUTING 로 상태값을 업데이트 합니다.
  4. Step이 성공적으로 종료되었다면 배치 처리가 완료되었다는 의미인 COMPLETED로 상태값을 업데이트 합니다.
     BatchStatus: COMPLETED, ExitStatus: COMPLETED 
  5. 모든 Step들이 마무리되었으면 마지막 Step의 상태값으로 상태값을 업데이트 합니다.

 

 

1. BatchStatus=”STARTING”, ExitStatus=”UNKNOWN”

Job을 실행하기 전에 JobInstance, JobParameter와 같은 Job 실행을 위한 객체를 생성하는데

이때 JobExecution 객체도 생성 됩니다.

 

SimpleJobRepository.createJobExecution()을 통해 JobExecution 객체가 생성이 됨

// JobExecution 생성
JobExecution jobExecution = new JobExecution(jobInstance, jobParameters, null);
jobExecution.setExecutionContext(executionContext);
jobExecution.setLastUpdated(new Date(System.currentTimeMillis()));

배치 처리작업은 아직 시작되지 않았으므로 ExitStatus값은 "UNKNOWN"으로 업데이트를 진행하고

BatchStatus는 배치 실행 전임을 의미하는 "STARTING"으로 값을 업데이트 합니다.

 

 

2. BatchStatus=”STARTED”, ExitStatus=”UNKNOWN”

Job을 실행하기 위한 준비가 완료되면 Job을 실행합니다.

 

SimpleJob.execute() 에 의해 Job이 실행됨

@Override
public final void execute(JobExecution execution) {
    ...  // 생략
    try {

        jobParametersValidator.validate(execution.getJobParameters());

        if (execution.getStatus() != BatchStatus.STOPPING) {

            execution.setStartTime(new Date());
            updateStatus(execution, BatchStatus.STARTED);  // STARTED로 배치상태 업데이트

            listener.beforeJob(execution);

            try {
                doExecute(execution);   // Job을 실행
                if (logger.isDebugEnabled()) {
                    logger.debug("Job execution complete: " + execution);
                }
            } catch (RepeatException e) {
                throw e.getCause();
            }
        } 
    ...  // 생략
    }
}

SimpleJob.execute() 메서드를 통해 Job을 실행 하는데요.

 

배치를 실행하기전에 validate()를 통해 JobParameter를 검증하고

배치 실행에 문제가 없다면 updateStatus(execution, BatchStatus.STARTED); 를 통해 배치상태값을 “STARTED”로 업데이트 하면서 doExecute()를 통해 실제 Job 실행합니다.

 

 

3. BatchStatus=”STARTED”, ExitStatus=”EXECUTING”

Job이 실행되면 Job에 구성되어 있는 Step이 순서대로 실행이 되게 됩니다.

Job과 Step은 1:N의 관계여서 Step을 여러개 구성이 가능하지만 여기서는 Job:Step=1:1 구성으로 진행합니다.

Step이 여러개가 있다면 3~4번과정이 각 Step마다 반복되서 일어난다고 생각하시면 됩니다.

 

 

TaskletStep.execute()

@Override
public final void execute(StepExecution stepExecution) throws JobInterruptedException,
UnexpectedJobExecutionException {

    Assert.notNull(stepExecution, "stepExecution must not be null");

    if (logger.isDebugEnabled()) {
        logger.debug("Executing: id=" + stepExecution.getId());
    }
    stepExecution.setStartTime(new Date());
    stepExecution.setStatus(BatchStatus.STARTED);  // BatchStatus 값 설정
    Timer.Sample sample = BatchMetrics.createTimerSample();
    getJobRepository().update(stepExecution);

    // Start with a default value that will be trumped by anything
    ExitStatus exitStatus = ExitStatus.EXECUTING;  // ExitStatus 값 설정

    doExecutionRegistration(stepExecution);
    ...
}

Step의 경우 TaskletStep.execute() 메서드로 실행됩니다.

Job을 실행할때와 비슷하게 Step을 실행하게 되면 배치 상태값을 업데이트 해주게 되는데요.

배치 상태는 여전히 실행중이기 때문에 "STARTED"값을 유지하고

STEP을 통해 배치 처리 작업을 진행하므로 ExitStatus값만 "EXECUTING"으로 업데이트 합니다.

 

4. BatchStatus=”COMPLETED”, ExitStatus=”COMPLETED”

TaskletStep.execute()

@Override
public final void execute(StepExecution stepExecution) throws JobInterruptedException,
UnexpectedJobExecutionException {
	... // 생략
	try {
        doExecute(stepExecution);
    }
    catch (RepeatException e) {
        throw e.getCause();
    }
    exitStatus = ExitStatus.COMPLETED.and(stepExecution.getExitStatus());  // ExitStatus 업데이트

    // Check if someone is trying to stop us
    if (stepExecution.isTerminateOnly()) {
        throw new JobInterruptedException("JobExecution interrupted.");
    }

    // Need to upgrade here not set, in case the execution was stopped
    stepExecution.upgradeStatus(BatchStatus.COMPLETED);  // BatchStatus 업데이트
    if (logger.isDebugEnabled()) {
        logger.debug("Step execution success: id=" + stepExecution.getId());
    }
    ... // 생략
}

Step 실행 성공

  • 정상적으로 Step단계가 종료되었다면 배치 상태값을 "COMPLETED"로 업데이트 해줍니다.
  • 아직 실행할 Step이 남아있다면 다음 Step 단계로 넘어갑니다

Step 비정상 종료

  • 만약 Step이 정상적으로 성공하지 않았다면 FAILED나 상황에 맞는 값으로 BatchStatus, ExitStatus를 업데이트 합니다.
  • 아직 실행할 Step이 남아있어도 이후의 Step을 진행하지 않고 즉시 배치처리를 종료합니다.

 

 

5. BatchStatus=”COMPLETED”, ExitStatus=”COMPLETED”

Step 실행이 모두 마무리가 되면 마지막 Step의 StepExecution의 상태 값이랑 동일하게 JobExecution 상태값을 업데이트 합니다.

 

protected void doExecute(JobExecution execution) throws JobInterruptedException, JobRestartException, StartLimitExceededException {
    StepExecution stepExecution = null;
    for (Step step : steps) {
        stepExecution = handleStep(step, execution);
        if (stepExecution.getStatus() != BatchStatus.COMPLETED) {
            //
            // Terminate the job if a step fails
            //
            break;
        }
    }

    //
    // Update the job status to be the same as the last step
    //
    if (stepExecution != null) {
        if (logger.isDebugEnabled()) {
            logger.debug("Upgrading JobExecution status: " + stepExecution);
        }
        execution.upgradeStatus(stepExecution.getStatus());
        execution.setExitStatus(stepExecution.getExitStatus());
    }
}

Job과 Step은 1:N의 관계이지만 JobExecution 상태값은 모든 Step을 다 실행하고 제일 마지막의 StepExecution 상태 값으로 배치 상태값을 업데이트 하는것을 볼 수 있습니다.

 

 

마무리

배치가 한번 실행될때 스프링 배치 내부적으로 BatchStatus, ExitStatus값들이 어떻게 변화하는지 공부해 보았습니다.

 

이 글을 통해서 상태값 변화의 흐름을 이해하신다면 실제 구성한 배치서비스에서도 쉽게 디버깅을 통해 상태값 변화를 관찰 하실 수 있고 STATUS, EXIT_CODE값만 보고도 배치 작업이 어떻게 진행되었는지 유추가 가능해질거라고 생각합니다.

이 글이 나중에 배치 상태값을 통해 배치 처리작업 결과를 이해하는데 도움이 되길 바라겠습니다!!

 

참고자료

 

반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함