Spring/Spring Batch

[Spring Batch] 5장 : Configuring a Step - 4 : Controlling Step Flow ~ Batch Status Versus Exit Status

공대키메라 2023. 12. 20. 22:31

지난 시간에는 StepExecutionListener ~ Example Tasklet Implementation을 읽어보았다

(지난 내용은 여기 클릭!)

 

이번 시간에는 Controlling Step Flow부터 다시 읽어볼 것이다.

 


Controlling Step Flow

가지고 있는 job안에서 step을 함께 그룹핑 하는 능력이 한 step에서 다른 step으로 어떻게 job이 "흘러가는지" 제어할 필요가 있다. Step의 실패는 Job 이 실패한다는것을 꼭 의미하는건 아니다. 또한 다음에 어떤 단계를 실행해야 하는지 결정하는 "성공" 유형이 두 가지 이상 있을 수 있다. Step의 그룹이 어떻게 설정되냐에 따라서 특정 step이 전혀 처리되지 않을 수 도 있다.

 

1. Sequential Flow

가장 간단한 flow 시나리오는 모든 step들이 순서대로 실행되는 job인데, 다음과 같다.

 

Figure 4. Sequential Flow

 

이것은 step에서 next를 이용해서 구현할 수 있다.

다음 예시는 Java에서 어떻게 next() 메소드를 사용하는지 보여준다.

 

@Bean
public Job job(JobRepository jobRepository) {
	return new JobBuilder("job", jobRepository)
				.start(stepA())
				.next(stepB())
				.next(stepC())
				.build();
}

 

위 시나리오에서 stepA가 첫 번째이기 때문에 먼저 작동한다. 만약 stepA가 정상적으로 완료되면, stepB가 작동한다. 하지만 step A가 실패하면 전체 Job은 실패하고 stepB는 실행하지 않는다.

 

여태 글을 열심히, 아니 대강 읽었어도 job안에 step이 어떻게 작동하는지는 예시 코드에서 매우 쉽게 알 수있을 것이다. flow라는 것은 결국 step의 작동 흐름을 말하는 것으로 보인다. 말 그대로 step flow인 것이다.

그러면 flow를 무엇이라고 정리하면 좋을까?

Spring Batch에서 "flow"는 일련의 단계(스텝)를 정의하고, 이들이 어떻게 연결되어 실행될지를 지정하는 개념이라고 정리하면 될 듯 한다. (GPT에게 물어봄 ㅎ)

그러면 그냥 가는 flow를 다양한 상황에 맞춰서 제어하고 싶을 때도 있을 것이다.
과연 그런지... 생각해보는 순간 다음 Conditional Flow에 대해 설명하고 있다.

 

2. Conditional Flow

앞선 예시에서는 오직 두 가지의 가능성이 있다.

 

1. step이 성공적이면 다음 step이 실행되야 한다.

2. step이 실패하면 job도 실패한다.

 

많은 경우 이정도로도 충분하다. 하지만, step의 실패가 실패를 하는 대신에 다른 step을 실행시키고 싶다면 어떨까? 다음 이미지는 그러한 flow를 보여준다. 

 

Figure 5. Conditional Flow

 

Java API 는 flow와 step 실패시 무엇을 하는지 구체화할 수 있는 많은 메소드를 제공한다. 다음 예시는 하나의 step(stepA)을 어떻게 구체화하고 두개의 다른 step을 stepA의 성공 여부에 따라서 처리하는지 보여준다.

 

@Bean
public Job job(JobRepository jobRepository) {
	return new JobBuilder("job", jobRepository)
				.start(stepA())
				.on("*").to(stepB())
				.from(stepA()).on("FAILED").to(stepC())
				.end()
				.build();
}

 

코드를 봐보면 음... stepA()가 먼저 실행이 되고 그 다음의 경우는 일반적으로 stepB가 실행되지만 stepA가 실패한 경우에는 stepC를 실행하도록 구성되어 있는 것 같다.

과연... 맞는지 확인해보자!

 

java 설정을 사용할 때 on() 메소드는 Step의 실행 결과인 ExitStatus와 매치시키기 위해 간단한 패턴 일치 스키마를 사용한다.

 

오직 두 개의 특별한 문자가 패턴에 사용된다. 

 

  • * : 0개 이상의 문자와 일치
  • ? :정확히 한 문자와 일치

예를 들어 c*t는 cat 그리고 count와 일치하는데 c?t는 cat과 일치하고 count와는 일치하지 않는다.

 

Step 에 변환 element의 수에 제한이 없지만 만약 Step실행 결과가 ExitStaus로 다룰 수 없다면 프레임워크는 에러를 던지고 Job을 실패처리한다.

 

3. Batch Status Versus Exit Status

conditional flow에서 Job을 설정할 때 BatchStatus와 ExitStauts사이의 차이점을 이해하는것이 중요하다. BatchStatus는 JobExecution과 StepExecution 둘 다의 열거형 속성이고 Job와 Step의 상태를 기록하기 위해 프레임워크레서 사용된다. 다음과 같은 값일 수 있다 : COMPLETED, STARTING, STARTED, STOPPING, STOPPED, FAILED, ABANDONES, UNKNOWN.대부분은 스스로 설명할 수 있다 : COMPLETED 는 step과 job이 완벽히 성공했을 때의 상태이고 FAILED는 실패했을 때 상태다. 

 

다음 예시가 Java 설정을 사용할 때 on element를 포함한다.

 

...
.from(stepA()).on("FAILED").to(stepB())
...

 

언뜻 보면 참조 시 해당 단계의 BatchStatus가 속한 것으로 보인다. 하지만 실제로 Step의 ExitStatus가 속한다. 이름에서 암시하듯이 ExitStatus는 실행을 끝낸 후에 Step의 상태를 나타낸다.

 

영어로 이것은 exit code가 실패하면 stepB로 가라고 말한다. 기본적으로 exit code는 항상 Step의 BatchStatus와 같은데 앞선 이것이 이전 항목과 동일한 이유이다. 하지만 만약 exit code가 달라야 한다면? 좋은 예시가 샘플 프로젝트의 skip sample job에 있다. 

 

다음 예시는 Java에서 다른 exit code로 어떻게 작업하는지 보여준다.

@Bean
public Job job(JobRepository jobRepository) {
	return new JobBuilder("job", jobRepository)
			.start(step1()).on("FAILED").end()
			.from(step1()).on("COMPLETED WITH SKIPS").to(errorPrint1())
			.from(step1()).on("*").to(step2())
			.end()
			.build();
}

 

step1는 세 개의 가능성을 가진다. 

 

  • Step이 실패하면 job은 실패해야 한다. 
  • Step이 성공적으로 완료된다.
  • Step이 성공적으로 완료되지만 COMPLETED WITH SKIPS의 exit code를 가진다. 이러한 경우 다른 step이 error를 다루기 위해 실행되야 한다.

앞선 설정은 작동한다. 하지만 다음 예시에서 보듯이 skipped records를 가진 실행 조건에 기반한 exit code를 변경할 필요가 있다.

 

public class SkipCheckingListener extends StepExecutionListenerSupport {
    public ExitStatus afterStep(StepExecution stepExecution) {
        String exitCode = stepExecution.getExitStatus().getExitCode();
        if (!exitCode.equals(ExitStatus.FAILED.getExitCode()) &&
              stepExecution.getSkipCount() > 0) {
            return new ExitStatus("COMPLETED WITH SKIPS");
        }
        else {
            return null;
        }
    }
}

 

 

앞선 코드는 Step이 성공한건지 먼저 체크하고 ExtepExecution의 생략 수가 0보다 큰지 StepExecutionListener이다.

두 개의 조건이 충족되면 새로운 COMPLETED WITH SKIPS의 exit code로 새로운 ExitStatus가 반환된다.

 

좀 더 해당 내용에 대해 이해하기 위해서 GPT에게 질문해보았다.
궁금하면 밑에 글을 보면 된다.

이건 개인적인 불만이지만 중요하다면서 차이점이나 세세한 것에 대해서 설명해주지 엉뚱한것을 설명하는지 모르겠다. 하여간...

 

BatchStatus과 ExitStatus 가 궁금하면 밑에 클릭!

더보기

해당 내용들은 내가 chatGPT에게 물어본 것들이다.  

 

BatchStatus

BatchStatus는 Spring Batch의 내부 실행 상태를 나타냅니다. JobExecution이나 StepExecution에 의해 반환된다. 이 상태는 Spring Batch 프레임워크가 작업의 생명주기를 관리하는 데 주로 사용한다. 주요 BatchStatus 값들은 다음과 같다.

  • COMPLETED: 작업이 성공적으로 완료되었음.
  • STARTING: 작업이 시작 중임.
  • STARTED: 작업이 실행 중임.
  • STOPPING: 작업이 중지 중임.
  • STOPPED: 작업이 중지됨.
  • FAILED: 작업이 실패함.
  • ABANDONED: 작업이 포기됨.
  • UNKNOWN: 작업 상태가 알려지지 않음.

BatchStatus는 작업의 실행 과정에서 어떤 상태에 있는지를 나타내며, 주로 작업의 성공, 실패, 실행 중 등의 상태를 나타냅니다.

ExitStatus

ExitStatus는 작업이 완료된 후의 상태를 나타내며, 외부 시스템이나 다른 작업에 의해 사용될 수 있는, 보다 상세한 상태 정보를 제공한자. 즉, 배치 작업의 실행 후 최종 결과를 나타낸다. ExitStatus는 BatchStatus보다 더 유연하며, 개발자가 커스텀 값을 설정하여 작업의 결과를 더 세밀하게 표현할 수 있습니다.

주요 ExitStatus 값들은 다음과 같습니다:

  • COMPLETED: 작업이 성공적으로 완료됨.
  • FAILED: 작업이 실패함.
  • 기타: 개발자가 정의한 커스텀 상태.

ExitStatus는 StepExecution 또는 JobExecution의 결과를 나타내며, 배치 작업 외부로 결과를 전달하는 데 주로 사용됩니다. 예를 들어, 작업의 결과에 따라 다른 작업을 트리거하거나, 시스템의 다른 부분에 성공 또는 실패의 상세한 정보를 제공하는 데 활용될 수 있습니다.

차이점 요약

  • 용도: BatchStatus는 Spring Batch 내부에서 작업의 실행 상태를 관리하는 데 사용되며, ExitStatus는 작업의 결과를 나타내고 외부로 전달하는 데 사용됩니다.
  • 세부 정보: ExitStatus는 BatchStatus보다 더 상세한 정보를 제공할 수 있으며, 개발자가 커스텀 값을 설정할 수 있습니다.
  • 역할: BatchStatus는 주로 프레임워크 내부에서 작업의 상태를 추적하는 데 중점을 두고, ExitStatus는 작업 완료 후의 결과를 나타내고 외부 시스템과의 상호작용에 중점을 둡니다.

 

다시 이를 정리하면...

  1. BatchStatus: Job이나 Step의 실행 과정에서 현재 상태를 나타냅니다. 예를 들어, Job이 실행 중일 때는 STARTED, 완료되었을 때는 COMPLETED, 오류로 인해 실패했을 때는 FAILED, 중단되었을 때는 STOPPED 등으로 나타납니다. Job의 실행이 중도에 실패하면, 해당 JobExecution의 BatchStatus는 FAILED로 설정됩니다.
  2. ExitStatus: Job이나 Step의 실행이 완료된 후 최종 결과를 나타냅니다. ExitStatus는 BatchStatus와 달리 사용자가 커스터마이즈 할 수 있어, 더 상세한 정보를 포함시킬 수 있습니다. 예를 들어, Job이 성공적으로 완료되면 COMPLETED, 실패하면 FAILED로 설정될 수 있지만, 사용자는 특정 조건에 따라 다른 문자열을 설정할 수도 있습니다.

 

4. Batch Status Versus Exit Status

BatchStatus와 ExitStatus의 논의 후에 BatchStatus와 Exitstatus가 job에서 어떻게 결정되는지 궁금할 수 있다.

이러한 상태들이 실행된 코드에 의해 Step에서 결정될 때 Job의 상태는 설정에 따라 결정된다.

지금까지 모든 잡 설정 논의들이 transition없이 최소한 하나의 final Step을 가진다.

 

* transition?

직역하면 이행으로, step이 다른 step으로 이동하는 것을 말한다.

 

다음 자바 예시에서, step이 실행한 후 Job이 종료된다.

 

@Bean
public Job job(JobRepository jobRepository) {
	return new JobBuilder("job", jobRepository)
				.start(step1())
				.build();
}

 

transition이 Step에서 정의되지 않으면, Job의 상태는 다음처럼 정의된다.

 

  • Step이 FAILED의 ExitStatus로 끝나면 Job의 BatchStatus와 ExitStatus는 둘 다 FAILED다.
  • 그렇지 않다면(그러니까 성공하다면 인 것 같다.), Job의 BatchStatus와 ExitStatus는 둘 다 COMPLETED다.

배치 잡을 종료하는 메소드가 몇몇 배치 잡(간단한 순서적인 step job 같이) 에게 충분하지만 custom으로 정의된 job-stopping 시나리오가 필요하다. 이러한 목적으로, 스프링 배치는 우리가 논의한 next 와 더불어 Job을 멈출 수 있는 세 개의 이행(transition) element를 제공한다. 이 각각의 멈춤 element는 Job을 특정한 BatchStatus에서 멈춘다. stop transition element가 Job의 어느 step들 안의 BatchStatus와 ExitStatus 에도 영향을 미치지 않는다는것 알아야 한다.

예를 들어, 하나의 job에서 step마다 FAILED의 상태를 가지지만 job에게는 COMPLETED 상태를 가지는 것이 가능하다.

 

그러니까 너무 헷갈리는게... Job안의 Step들이 실패 하더라도 Job은 COMPLETED로 처리할 수 있다는 말이다.
이게 어떻게 가능하지? 했지만 이것을 가능하게 해주는 것이 transition element라고 한다. (아 헷갈려...)

그러면 어느 상황에서 위와 같이 Step이 실패해도 Job은 성공시켜야 할까?

이것에 대해서도 GPT에게 물어봤는데 합리적인 답변을 내놓았다.

 

답변이 궁금하면 아래 클릭!

더보기
ME:
그러면 어느 상황에서 Step은 실패했는데 Job은 성공으로 해야하지? 나는 잘 모르겠는데? 굳이 그렇게까지?
 
ChatGPT:

Step이 실패했음에도 불구하고 Job을 성공(COMPLETED)으로 표시하는 경우는 일반적으로 특정 비즈니스 로직이나 요구사항에 의해 결정됩니다. 이러한 상황은 일반적이지 않지만, 특정 시나리오에서는 유용할 수 있습니다. 몇 가지 예시를 들어보겠습니다:

  1. 부분적 성공이 허용되는 경우: 어떤 배치 작업에서 모든 단계가 완벽하게 성공할 필요는 없지만, 전체 작업의 목적을 달성하는 데 충분한 경우입니다. 예를 들어, 대량의 데이터 처리 작업에서 소수의 레코드 처리에 실패했지만, 전체 작업의 대부분이 성공적으로 완료되었다면, 전체 Job을 COMPLETED로 마크할 수 있습니다.
  2. 오류 처리 메커니즘: 특정 Step에서 오류가 발생했을 때, 이를 적절히 처리하고 로깅한 후에도 전체 작업을 계속 진행하고자 할 수 있습니다. 예를 들어, 특정 데이터 변환 단계에서 오류가 발생했지만, 이후 단계에서 이 오류를 보완하거나 무시하고 작업을 계속 진행할 수 있습니다.
  3. 비즈니스 로직에 의한 선택적 Step 실행: 특정 Step의 실패가 다음 Step의 실행 여부를 결정할 수 있습니다. 이 경우, 실패한 Step이 다음 Step의 실행에 영향을 주지 않으므로, 전체 Job은 여전히 성공적으로 완료될 수 있습니다.
  4. 허용 가능한 오류: 특정 Step에서 발생하는 오류가 비즈니스 로직상 허용 가능한 범위 내에 있을 때, 이 오류를 무시하고 전체 Job을 성공적으로 완료할 수 있습니다.

이렇게 spring batch는 정말 다양한 상황에서의 기능을 지원하고 있다.

 


 

이번 시간에는 Configuring Step Flow에서 부터 Batch Status Versus Exit Status까지 읽어 보았다.

 

다음에는 Configuring a Step에서 남은 부분들을 다 읽어볼 것이다.

 

출처:

https://docs.spring.io/spring-batch/docs/5.0.4/reference/html/step.html