예약부터 확정까지 한 메서드에 적으면 흐름을 읽기 쉽다. 그러나 메서드를 실행하던 JVM이 사라지면 지역 변수와 다음에 실행할 문장의 위치도 사라진다. 예약은 외부 DB에 남아 있고 변환 결과 파일도 존재할 수 있지만, 애플리케이션을 다시 띄우는 것만으로 저장 단계부터 자동으로 이어지지는 않는다.
오케스트레이션의 핵심은 순서 결정이지만 운영에서는 그 결정을 중단 뒤에도 이어 갈 수 있어야 한다. 긴 대기나 재시도가 있는 업무라면 무엇이 끝났고 무엇을 기다리는지 보존해야 한다. 이 정보를 직접 관리할지 실행 관리 도구에 맡길지 판단하기 위해 먼저 작은 조정자의 책임을 적어 보자.
상태 이름 하나로는 부족하다
문서 상태에 PROCESSING만 기록하면 사용자는 진행 중이라는 사실을 알 수 있다. 하지만 복구 코드는 예약 전인지, 예약은 끝났지만 변환 전인지, 결과가 저장됐지만 확정 응답만 못 받은 것인지 구별할 수 없다. 어느 단계부터 재개해도 안전하도록 모든 참여자가 충분한 조회와 멱등 계약을 제공하거나, 더 구체적인 실행 정보를 저장해야 한다.
| 복구에 필요한 정보 | 필요한 이유 |
|---|---|
| 업무 식별자와 입력 버전 | 같은 업무와 다른 입력을 혼동하지 않기 위해 |
| 완료한 단계와 그 결과 참조 | 이미 성공한 효과를 다시 만들지 않기 위해 |
| 대기 중인 호출과 결과 불명 상태 | 응답 유실을 단순 실패와 구별하기 위해 |
| 다음 재시도 시각과 시도 정책 | 재시작 뒤 무제한 즉시 재시도를 피하기 위해 |
| 보상 진행과 남은 실패 | 정리가 끝나지 않은 업무를 식별하기 위해 |
이 표를 DB 테이블로 만드는 것만으로 복구 시스템이 완성되지는 않는다. 외부 호출과 상태 기록 사이에도 중단될 수 있다. 결과 저장이 성공한 직후 조정자의 STORED 상태 기록 전에 종료되면 조정자는 저장이 끝났는지 모른다. 이런 간격 때문에 외부 효과의 멱등성과 조회 계약이 계속 필요하다.
다시 실행하는 주체도 필요하다
상태가 남아 있어도 아무도 읽지 않으면 업무는 진행하지 않는다. 재시작한 조정자가 미완료 업무를 찾고 현재 소유자를 정한 뒤 다음 작업을 실행해야 한다. 여러 인스턴스가 동시에 같은 행을 가져갔을 때는 중복 실행을 막거나 안전하게 수습해야 한다. 이전 실행자가 정말 멈췄는지, 늦게 결과를 쓰는지까지 고려하면 단순한 실행 중 플래그 이상의 문제가 된다.
Timer도 비슷하다. 프로세스 내부에서 10분 동안 잠든 스레드는 프로세스 종료 뒤 그대로 남지 않는다. 대기 만료 시각을 저장하고 재시작 뒤 기한이 지났는지 확인하는 책임이 필요하다. 사용자 승인처럼 외부 메시지를 기다리는 경우에는 승인 도착과 현재 상태 변경의 순서도 다뤄야 한다.
직접 구현이 항상 잘못된 선택은 아니다. 흐름이 짧고 상태가 적으며 기존 작업 처리 기반이 있다면 필요한 범위를 직접 관리하는 편이 단순할 수 있다. 다만 코드에 보이는 정상 순서만 비교하고 상태 저장·대기·재시도·관측·변경 호환성 비용을 빠뜨리면 판단이 왜곡된다. 이번 시리즈에서는 이 책임을 적은 뒤 Temporal이 어느 부분을 제공하는지 비교한다.
Temporal은 메모리를 저장하는가
Temporal은 Workflow의 실행 이력을 보존하고 그 이력을 이용해 상태를 재구성한다. 사용자 코드가 실행되는 Worker와 이력을 관리하는 Service의 역할은 다르다. Worker 프로세스가 바뀌어도 호환되는 Workflow 코드가 이력을 재생해 진행할 수 있다. JVM의 메모리 전체나 실행 중이던 OS 스레드를 그대로 저장했다 복원하는 방식으로 이해하면 안 된다. Workflow Execution 설명을 읽을 때 이 구분을 먼저 잡는 것이 좋다.
그 대가로 Workflow 코드에는 재생과 맞는 실행 규칙이 있다. DB·HTTP 같은 외부 작업은 Activity에 두고, 이미 기록된 결과와 앞으로 수행할 작업을 구별한다. 완료가 기록된 Activity의 결과는 재생에 사용하지만, 외부 효과가 성공했어도 완료 보고가 기록되지 않았다면 Activity가 재시도될 수 있다. 이 두 상황을 모두 ‘처음부터 다시 실행’이라고 부르면 중요한 차이를 놓친다.
중단 위치를 정해서 비교한다
첫 실험에서는 실행 관리 기능이 없는 간단한 조정자를 사용한다. 예약이 끝나고 변환을 시작하기 전 프로세스를 종료한다. 재시작한 프로그램이 무엇을 보고 다음 단계를 결정할 수 있는지 확인한다. 임의로 처음부터 돌리기 전에 그 선택이 예약을 중복 생성할 수 있는지 따져 본다. 실제 실행 기록은 후속 실습에서 보강한다.
- 동일 업무 식별자로 예약을 만든다.
- 예약 완료 직후 프로세스를 종료한다.
- 새 프로세스에서 남은 데이터만 보고 현재 단계를 판단한다.
- 판단에 부족한 정보와 안전하게 재시작하기 위한 계약을 적는다.
예상 결과는 예약 존재만으로 전체 실행 위치를 완전히 알기 어렵다는 것이다. 기존 코드가 상세 상태를 이미 기록한다면 어떤 상태 전이와 재개 코드가 이 문제를 해결했는지 확인한다. 이후 Temporal판에서 같은 중단을 재현할 때는 Service 저장소가 살아 있고 호환되는 Worker가 다시 실행된다는 전제를 명시한다.
장애 복구의 목표는 프로세스를 빠르게 띄우는 것만이 아니다. 업무가 중복 효과 없이 의도한 상태로 진행하는지까지 확인해야 한다. 운영 health가 정상이라고 예약이 정리됐거나 결과 저장이 완료됐다고 결론낼 수 없는 이유도 여기에 있다.
이해 확인
업무 상태를 PROCESSING으로 저장하는 것과 실행 이력을 보존하는 것은 어떤 정보에서 다를까? 외부 저장은 성공했는데 완료 기록 전에 조정자가 종료되면, 재시작한 코드가 안전하게 판단하려면 무엇이 필요할까?
참고 자료
- Workflow Execution: 실행 수명과 이력 기반 실행 관리.
- Workflow Definition: 재생과 결정성의 제약.
- Activity Definition: 외부 효과와 재실행에 대한 계약.