[글또] 2. Spring Batch 간접 경험하기 - part2

jinvicky·2024년 10월 15일
1

Question

배치 애플리케이션을 분석하면서 이건 왜 이렇지?
어떻게 실행되며 동작하는 것이지? 라는 궁금증이 가득했다.

어떻게 실행되지?

배치는 REST API 방식으로 http 요청을 controller가 받아서 실행하는 구조가 아니다. (설정값 변경 등의 설정 목적을 위한 controller 제외)

그럼 어디서 띄우고 어떻게 동작하지?

📌 VM을 띄우고 .sh 명령어로 동작한다.

자바 애플리케이션을 실행할 때 사용되는 명령어들과 옵션들을 appOptions에 정의한다고 한다. 구글링을 했을 때는 무엇인지 검색했을 때 잘 안 나와서 copilot 돌려서 검색했다. (왜지..)
다음과 같은 형식으로 이루어져 있다.

start.sh (최상단의 script 폴더)
(폰트가 다른 부분은 $가 앞에 붙어있다)

appOptions="-Dfile.encoding=UTF-8 -Ddatasource.bind=runtime -Ddebug.mode=true -Dserver.id=serverIdDserver.port=serverId -Dserver.port=applcationPort -Dapplication.id=applicationIdDauto.run=applicationId -Dauto.run=autoRun -Dschedule.sleep=$scheduelSleep ...생략"

-Ddatasource.bind=runtime

: DB 연결과 같은 데이터 소스 바인딩 시점이 빌드 시점이 아니라 런타임 시점이라는 뜻이다.
이는 흔한 케이스가 아닌데, 보통은 배치를 돌릴 때 사전에 정해둔 데이터 소스 정보를 연결해서 실행하지, 런타임 시점에 원하는 db 정보를 또 가져와서 설정하지 않기 때문이다.

-Dschedule.sleep=false

: 위 키값이 true면 스케줄 관련이 대기 상태로 들어가기 때문에 false인 것이 좋다.

-Dauto.run=$autoRun

메인 애플리케이션을 실행하면 가장 먼저 실행되는 애플리케이션을 master로, 나중의 그 외의 것들을 slave로 간주하고 애플리케이션들을 강제로 전부 실행시킨다.
목적은 애플리케이션이 n개 실행되는데 일일이 그것을 띄우는 것이 번거롭기 때문에 일괄처리하기 위함이다.

그럼 또 여기서 파라미터들을 받는 게 어디로부터인가? 궁금해진다.
젠킨스를 한번 볼까? 자세히 다 말할 수는 없으니 조금만.

젠킨스에서 파라미터와 함께 빌드를 선택할 때 뜨는 옵션들이다.
필요한 포트, 애플리케이션 id, server id 등이 아래에 args 인자로 들어간다고 한다.

@Override
	public void run(ApplicationArguments args) { // 여기에 들어간다. 
		setGlobal(args);
		setApplicationStatus(args);
		// ... 중략
		batchJobManager.initBatchJob(args);
	}

애플리케이션에서 스크립트 .sh 파일들을 보면 (예시로 start.sh)

# 디폴트 파라미터 설정
if [ "$1" = "" ]; then
	serverId=서버아이디
...

if [ "$2" = "" ]; then
	applicationId=애플리케이션아이디
else
	applicationId=$2

if [ "$3" = "" ]; then
	applcationPort=8080
else
	applcationPort=$3

여기에 나오는 $1, $2, $3이 앞서 젠킨스에서 선택한 파라미터에 해당한다.

메인 애플리케이션 클래스는 어디에 있지?

보통 최상단의 Application 클래스에서 배치 관련 설정 로직들이 들어가 있는데, 우리 배치는 별도의 애플리케이션 클래스를 가지고 있었다. 궁금한 점은 이 애플리케이션 클래스를 호출하는 코드는 어디에 있는 거지? 였다.

api를 통해서 batch를 관리하려고 만든 controller에서 호출하는 것 말고는 어디서도 메인에서 호출 또는 실행하는 부분이 보이지 않아서 시니어께 질문했고, ApplicationRunner를 알게 되었다.

public class LdcApplication implements ApplicationRunner {

📌 ApplicationRunner를 구현한 클래스는 메인 Application 클래스가 실행될 때 메인에서 직접적으로 import해서 호출하지 않더라도 자동으로 실행된다.
run() 메서드를 오버라이딩하는데 이때 인자로 받은 ApplicationArguments args는 실제 젠킨스로 배포할 때 .sh에서 입력한 파라미터들이며, 이들을 받아서 배치에 사용한다고 한다.

Transaction 처리는 어떻게 이루어져 있지?

내가 아는 트랜잭션은 스프링의 @Transactional 어노테이션을 이용하는 것이다. 다만 이 어노테이션은 auto_increment 속성 컬럼의 경우 제어가 되지 않는다는 점이 특징이다.
(실제로 학원 프로젝트 당시 회원 테이블과 회원 배송지 테이블을 별도로 두었을 때, 배송지 테이블에 실패했음에도 회원의 pk가 auto_increment였는데 롤백이 되지 않아서 난처했던 경험이 있다.)

스프링에서 db 연동을 위해 MyBatis를 사용할 때 MapperInterface 방식과 SqlSessionTemplate 방식을 사용할 수 있다.

원래 autoCommit 속성이 true면 명령어별로 즉시 커밋이 실행되지만, 배치 내부 로직상 묶어서 일괄 커밋하는 것이 더 적합했다.

sqlSesstionTemplate으로부터 커넥션을 얻어오면 Connection 객체에 담는다.
Connection 클래스는 setSavePoint()를 통해서 랜덤 uuid로 savePoint를 설정할 수 있도록 지원한다.
(검색하니 JDBC 관련으로 많이 나온다)

또한 java.sql.SavePoint 를 통해서 트랜잭션 특정 지점을 지정해서 롤백, 커밋의 시점을 관리할 수 있다.

Connection connection = this.sqlSession.getConnection();

connection.setAutoCommit(false);
Savepoint savePoint = connection.setSavePoint(UUID.randomUUID().toString());
connection.commit();
connection.rollback(Savepoint sp); // 기타 등등....

🔥 Issue - delete, insert시 Slave 지연

학습분석을 대량으로 수집 후 delete + insert 과정을 거치는데, 이 때 인프라팀으로부터 Slave DB들의 지연이 크게 발생한다는 이슈 문의가 들어왔다.

정리하면 Master DB가 커밋되면 Slave DB가 명령 실행을 시작하는데 마스터가 100건 이후 커밋되면 슬레이브는 그때까지 멍하니 기다리기만 하니(=지연) 마스터가 10건(변경 가능) 실행 후에 커밋되도록 해서 슬레이브 지연을 줄이자는 뜻이다.

📌 insert 쿼리는 위와 같이 처리하고, delete 명령은 delete말고 truncate로 쿼리를 변경했다. delete보다 truncate가 속도가 더 빠르다.

그래서 내부적으로 prograssiveCommit() 메서드 등을 만들어서 indexCount를 세서 10일 경우 자동 커밋후 신규 SavePoint를 반환하도록 구현이 되었다.

🔥 Issue - 신규 job을 추가했는데 왜 안 돌아가지?

(모든 배치 일괄 적용 가능한 이슈가 아닙니다.)

배치의 시작은 Batch Sever DB 테이블이다. DB 테이블에 서버 정보부터 배치 서버당 할당할 애플리케이션 id들까지 각각 테이블들로 관리한다.

기존 배치에 신규로 잡을 추가하고 싶어서 잡 테이블에 신규 데이터를 추가했는데 왜 돌아가지 않지? 시니어 왈, 이 배치는 옵션 정보들을 일괄로 맞추지 않아서라도 한다.

📌 배치를 분석할 때 잡과 동시에 잡과 연관된 옵션들을 같이 체크하고 질문할 필요가 있다는 걸 알게 되었다.
이 잡은 어떤 옵션들과 연결되어 있을까?
배치 서버당 애플리케이션은 1:1일까? 1:n일까? (확장성 고려하면 후자겠지?)

Retrospect

평소에 굉장히 질문을 많이 하는 편인데, 질문은 생각보다 쪽팔림을 무릅써야 할 때가 많다. 얘는 왜 이런걸 물어보지? 얼굴을 마주쳐야 하기 때문이다.

기술 블로그 등에서 메타 인지, 즉 내가 아는 것과 모르는 것을 아는 능력이 중요하다고 봤는데, 아직 나는 어떤 질문이 좋은 질문인지 모르겠다. 선배는 니 능력만큼 나한테서 뽑아가는 거야 라고 하셨는데 아직 구체적인 것이 안 잡힌다...

profile
Front-End와 Back-End 경험, 지식을 공유합니다.

0개의 댓글