Pandaman Blog

[Redux-saga 배경지식]Saga란 무엇인가? 본문

Front end/Redux-Saga

[Redux-saga 배경지식]Saga란 무엇인가?

oyg0420 2021. 1. 28. 21:49

Saga란 무엇인가?

1. 탄생의 배경

Saga는 본래 Long Live Transaction(LLT)의 문제점을 해결하기 위한 개념이다. LLT는 상대적으로 긴 시간 동안 데이터베이스 리소스를 사용하여 다른 트랜잭션의 종료를 지연시키는 문제가 있다. 많은 객체에 접근하기 때문에 많은 교착상태를 발생키며, 지연 가능성이 굉장히 높다.

2. Saga란?

특정 애플리케이션의 경우 데이터베이스의 일관성을 희생하지 않고 특정 LLT가 완료되기 전에 리소스를 기다리고 있는 트랜잭션들을 허가할 수 있다.
예를 들어서 확인해보자.

항공권 예약 트랜잭션을 (T)라고 생각해보면, 이 트랜잭션이 완료할 때까지 모든 리소스를 붙잡고 있을 필요는 없다. 
비행기 좌석에 대한 예약을 T1이라고 하면 T1이 끝나고 바로 좌석 예약에 대한 다른 트랜잭션들을 즉각적으로 허가할 수 있다. 
다른 말로 T는 하위 트랜잭션을 컬렉션이라고 할 수 있다.

예제를 보고 Saga대해 대충 감이 오지 않는가?
첫 번째, Long Live Transaction(LLT)이 연속적인 트랜잭션으로 쓰인다면 이것을 Saga라고 한다.
두 번째, Saga는 하위 트랜잭션의 Collection이라고 할 수 있다.

하지만 DBMS 입장에서는 트랜잭션이 모두 독립적인 것을 원하지 않는다. 데이터베이스의 원자성 때문에 예약이 모두 되었거나, 아니면 모두 취소되었거나 둘 중 하나를 원한다. 그렇다면 하위 트랜잭션 중 하나라도 취소된다면 Saga는 어떠한 형태로 일관성을 유지할까?
Saga는 서로 연관된 트랜잭션이다. 하나의 트랜잭션이라도 바라는 방향이 아니라면, 반드시 보상을 받는다.
이게 무슨 말일까? 한마디로 트랜잭션 T1가 실패하면 보상 트랜잭션 C1가 발생한다. 즉, T1를 취소하는 트랜잭션을 발생하는 것을 의미한다. 그렇게 한다면 데이터의 일관성을 유지되지 않는가. 이제 Saga의 세 번째 특징이다.
세 번째, Saga는 트랜잭션이 실패했을 때 관리하는 패턴을 갖는다. Saga는 트랜잭션 T와 함께 보상 트랜잭션(C)을 갖는다. 보상 트랜잭션 C는 의미상으로 트랜잭션 T를 실행을 취소하는 것이다. 보상 트랜잭션은 T가 수행한 모든 작업을 취소하여 시스템의 일관성이 유지된다.
이때 알아할 점은 트랜잭션 T가 실패하고 보상 트랜잭션 C가 발생했을 때 데이터 베이스의 상태가 트랜잭션 T가 발생했을 때의 상태로 돌아가는 것은 아니다. 트랜잭션 T가 발생하는 동안 다른 트랜잭션들도 발생한 경우도 있기 때문에 데이터의 상태가 동일하다고 볼 수 없기 때문이다.

3. Saga의 command와 동작원리

Saga 명령어는 begin-saga, abort-saga, end-saga로 나뉜다.
begin-saga는 말 그대로 시스템에서 saga가 시작되는 것을 의미한다.
begin-saga 명령에는 begin-transaction과 end-transaction 명령들이 포함되어 있다.
현재 실행 중인 실행 중인 트랜잭션과 보상 트랜잭션을 발생시키는 abort-saga 명령이 존재한다.
마지막으로 현재 실행 중인 트랜잭션을 커밋하고 Saga를 완료하는 end-saga 명령이 존재한다.
abort-transaction 명령은 트랜잭션이 중지 후 사가를 실행할 주소를 포함하고 있다.
end-transaction 명령은 트랜잭션이 롤백되는 경우에 대해 보상 트랜잭션에 대한 식별자를 포함하고 있다.
마지막으로 abort-saga 명령은 save-point 식별자를 갖고 있다.
save-point는 saga가 실패하거나 시스템이 충돌되었을 때 모든 트랜잭션에 대해 보상하는 대신 마지막 save-point 이후에 다시 saga를 재시작하게 하여 이러한 과정을 크게 줄일 수 있다.

4. 안정적인 코드 저장

충돌이 일어났을 때 saga 프로세스를 완료하기 위해서는 누락된 트랜잭션뿐만 아니라 보상 트랜잭션 모두 필요하다.
두 경우 모두 애플리케이션 코드가 존재해야 한다. 추상 데이터 유형의 트랜잭션 시스템은 비슷한 문제와 직면해 있다.
추상 데이터 객체의 복구는 데이터 유형에 대한 로깅 작업이 포함된다.
이러한 작업을 구현하기 위한 코드가 데이터 베이스에 존재해야 충돌 후 일관된 데이터 상태를 유지한다.
첫 번째, 기존 데이터베이스의 시스템 코드 처리할 때 응용프로그램 코드를 처리하는 것입니다.(DBMS는 장애로 인한 시스템에 필요한 코드 파괴를 방지하기 위해 시스템 코드를 저장해야 한다.)
두 번째, Saga 프로세싱에서 saga에 대한 애플리케이션 코드가 기존 시스템 코드와 동일한 방식으로 정의되고 업데이트되도록 할 수 있다. saga가 실행되기 시작하면 모든 트랜잭션과 보상 트랜잭션이 미리 정의되어 있다고 가정하고 단순히 응용프로그램을 호출한다. 업데이트는 DBMS에 의해 제어되는 것이 아니기 때문에 업데이트 도중 충돌이 발생했을 경우에 수동으로 작업을 해야 한다.
위 방법은 신뢰할 수 있는 애플리케이션 개발자에 의해 사용될 경우를 말한다. 이러한 경우가 아니라면,
세 번째, Saga 코드를 데이터베이스의 일부로 처리하는 것이 좋다. saga 코드가 하나 또는 여러 데이터베이스 객체로 저장되어 있다면 saga의 회복은 자동으로 처리된다.
네 번째, DBMS에 코드를 관리할 수 있다면, 사가의 안정적인 코드 저장은 꽤 간단하다.

5. 역방향 복구

DBMS에서는 Saga Execution Component(SEC)가 Saga를 관리한다. 그리고 SEC는 독립적인 트랜잭션을 관리하는 Transaction Execution Component(TEC)를 호출한다. SEC는 일련의 트랜잭션들을 하나의 유닛으로 실행한다. 반면에 TEC는 일련의 액션들을 하나의 유닛으로 실행한다.
SEC와 TEC는 모두 saga와 트랜잭션의 행동에 대한 로그를 저장한다.
모든 saga명령과 데이터 베이스의 액션은 SEC를 통해 이루어진다. 모든 Saga 명령은 액션이 요청되기 전에 로깅된다.
마찬가지로 begin-transaction, end-transaction 명령과 같이 데이터 베이스 액션은 TEC로 이동되고 TEC에 의해 핸들링된다.

SEC가 abort-saga 명령을 받았다고 가정해보자. 그럼 역방향 복구(backward recovery)가 시작된다.
T1, T2 트랜잭션이 진행되었고, T3이 진행중이다. 이때 abort-saga 명령이 SEC로 요청되었다.
SEC는 해당 요청을 로깅하고 TEC에게 현재 진행중인 T3를 중지하라고 지시한다.
그리고 T3를 롤백된다. 다음 SEC는 로그를 참조하고 보상 트랜잭션 C2와 C1의 실행을 명령한다.
이 트랜잭션들이 시작되고 종료될때의 로그들은 모두 TEC에 의해 기록된다. 
트랜잭션 C1이 커밋되면 saga는 끝이난다. 로그에는 end-saga가 기록된다.

기록되는 로그는 굉장히 중요하다. 충돌에서 복구하는 데 사용되기 때문이다. 충돌 후 TEC는 먼저 보류 중인 트랜잭션을 정리하기 위해 호출된다. 트랜잭션이 정리되면(중단 또는 커밋) SEC는 Saga의 상태를 확인한다. begin-saga 및 end-saga가 로그에 기록되어 있다면 Saga가 완료된다. 하지만 end-saga가 누락되었다면 Saga는 중단되고 로그를 스캔하여 SEC는 마지막으로 성공된 트랜잭션과 보상받지 못한 트랜잭션을 발견한다. 그리고 해당 트랜잭션과 앞선 트랜잭션에 대해 보상 트랜잭션이 발생한다. 이것이 바로 역방향 복구이다.

5. 순방향 복구

순방향 복구를 위해서 SEC는 누락된 트랜잭션에 대한 신뢰 있는 코드와 sava-point가 필요하다.
save-point는 애플리케이션과 시스템에 의해 지정되는데, abort-saga와 관련 있다(abort-saga 명령은 sava-point 식별자를 반환).

다음과 같이 트랜잭션들이 실행되었다. T1 -> T2 -> sava-point command -> T3 
T4 트랜잭션이 작동하는 동안 시스템이 충돌하였다. 
회복을 위해 시스템은 첫번째로 역방향 복구를 save-point까지 진행한다.(C3)
T3, T4 작동 코드가 사용가능하다면, SEC는 로그에 restart를 기록하고 Trasaction을 다시 시작한다. 

우리는 이것은 Backward/forward Recovery라고 말한다.

6. DBMS에서의 Saga 구현

DBMS에서 Saga를 실행하기 위해 두가지가 필요하다.
첫째, 애플리케이션 코드에 포함된 Saga 명령으로 서브 루틴이 호출된다. 서브 루틴은 SEC가 로그에 저장한 모든 데이터를 데이터베이스에 저장한다.
예를 들어, begin-saga 서브 루틴은 데이터베이스의 테이블의 사가 대한 식별을 입력한다. save-point서브 루틴은 애플리케이션이 유사한 데이터베이스 테이블에 상태를 저장하도록 한다. end-transaction 서브루틴은 다른 테이블에 트랜잭션 끝, 보상 트랜잭션에 대한 식별을 입력한다.
데이터베이스에 Saga정보를 저장하는 명령은 항상 트랜잭션 내에서 수행되어야 한다. 충돌시 정보의 손실이 있을 수 있다.
saga의 서브루틴은 saga가 현재 트랜젹션을 실행 중인지 여부를 추적해야한다.

두번째, SD(Saga Deamon)이 필요하다. SD는 보류중인 saga의 상태를 확인하면 보상 트랜잭션 또는 트랜잭션을 발생시킨다. 트랜잭션이 중단된 경우 SD는 saga 테이블를 주기적으로 스캔하고 발견되면 수정 조치가 취해진다.

7. 병렬 Saga

saga 트랜잭션은 동시에 실행되도록 할 수 있다. saga 프로세스는 새 프로세스와 병렬로 실행할 수 있다고 가정한다. 시스템은 saga와 프로세스를 결합하는 기능을 제공할 수 있다.
역방향 크래시 복구는 순차 saga의 경우와 유사하다. 역순으로 보상(또는 실행취소)가 된다. 하위 프로세스가 상위 프로세스보다 먼저 보상 트랜잭션이 발생한다.
역방향 복구: abort-saga를 요청한다면 SEC는 모든 포함된 Saga의 프로세스를 중단시킨다. 그런 다음 보류 중인 모든 트랜잭션을 중단하고 커밋된 모든 트랜잭션을 보상한다.
순방향 복구: save-point로 인해서 이다. save-point의 일관성을 유지하려면 SEC는 반드시 forking and joining을 해야 한다. 모든 정보를 로그에 기록되고 SEC는 가장 최신의 save-point를 선택하고 만약 save-point가 없다면 모든 프로세스를 롤백한다.

8. CQRS(Command and Query Responsibility Seperation)

데이터 저장소에 대한 읽기 및 업데이트 작업을 분리하는것을 의미한다. 이러한 분리로 즉각적인 이점은 단일 책임원칙을 적용하여 코드를 명확하고 단순화하는 것이다. 개체는 데이터의 수정 또는 데이터 쿼리(조회)를 담당한다.

1) CQRS의 Saga

제한된 컨텍스트와 집합간에 메세지를 조정하고 라우팅하는 코드를 지칭하며 프로세스 매니저라고 불릴 수 도 있다.
CQRS 패턴를 구현할때 일반적으로 명령과 이벤트 정보를 교환하다. 명령은 작업을 수행하도록 요청하는 것이고 이벤트는 어떤 일이 일어났음을 알리는것이다. 프로세스 매니저가 존재하는 비지니스 프로세스에서는 집합들이 직접 메세지를 주고 받을 필요가 없고 오직 메세지는 프로세스 매니저에 의해 라우팅된다. 따라서 각각의 집합들은 다음 단계의 프로세스가 무엇인지 알 필요가 없다.

아래의 경우 프로세스 관리자를 사용하는 것이 좋다.

  • 제한된 컨텍스트가 집합체 간의 수집 지점 간 상호 작용으로 관리하기 어려운 많은 이벤트 및 명령을 사용하는 경우.
  • 제한된 컨텍스트에서 메시지 라우팅을 더 쉽게 수정하려는 경우. 프로세스 관리자는 라우팅이 정의 된 단일 위치를 제공합니다.

'Front end > Redux-Saga' 카테고리의 다른 글

[Redux-saga] pulling future actions  (0) 2021.02.18
[Redux-saga] Helper 함수  (0) 2021.02.13
Redux-Saga 란  (0) 2021.01.21
Comments