보상 트랜잭션 (Compensating Transaction)
보상 트랜잭션은 마이크로서비스 아키텍처에서 분산된 트랜잭션을 롤백하거나 복구하기 위해 사용되는 메커니즘입니다. MSA에서는 여러 서비스 간의 트랜잭션을 일관성 있게 유지하기 어렵기 때문에 보상 트랜잭션이 필요하게 됩니다.
보상 트랜잭션의 원리
1. 원본 트랜잭션 수행
보상 트랜잭션은 기본적으로 어떤 작업을 수행하기 전에 원본 트랜잭션을 수행합니다. 이 트랜잭션은 여러 서비스 간에 분산되어 수행되는 경우가 많습니다.
2. 보상 로직 정의
각 트랜잭션은 보상 로직을 정의합니다. 이 보상 로직은 원래 트랜잭션이 실패할 경우 실행됩니다. 보상 로직은 주로 역으로 원래 작업을 취소하거나, 복구하는 작업을 수행합니다.
3. 보상 트랜잭션 실행
원본 트랜잭션에서 문제가 발생하면 보상 트랜잭션이 실행되어 이전 작업을 롤백하거나 복구합니다.
보상 트랜잭션의 이점
1. 일관성 유지
여러 서비스 간에 분산된 트랜잭션에서 일관성을 유지하는 것은 어려울 수 있습니다. 보상 트랜잭션은 실패한 트랜잭션을 롤백하거나 복구함으로써 일관성을 유지할 수 있습니다.
2. 롤백 대신 복구
일부 작업은 롤백이 불가능할 수 있습니다. 이런 경우에는 보상 트랜잭션을 사용하여 원래 작업을 롤백하는 대신 복구할 수 있습니다.
3. 역순 작업 실행
보상 트랜잭션은 원래 작업의 역순 작업을 수행함으로써 실패한 트랜잭션을 복구합니다. 이는 특히 데이터베이스의 변경 사항을 원래 상태로 되돌리는 데 유용합니다.
보상 트랜잭션의 한계
1. 복잡성 및 유지보수 어려움
보상 트랜잭션은 추가적인 로직이 필요하며, 복잡한 시나리오에서는 어려워질 수 있습니다. 보상 로직을 설계하고 구현하는 것은 어려울 수 있으며, 변경 사항이 발생할 때 유지보수가 어려울 수 있습니다.
2. 경합 상태 발생 가능성
보상 트랜잭션을 사용하면 여러 서비스 간에 경합 상태(race condition)가 발생할 수 있습니다. 여러 서비스에서 동시에 보상 로직을 실행하면 예측할 수 없는 결과가 발생할 수 있습니다.
3. 비동기 통신 문제
보상 트랜잭션은 실패 상황을 즉시 감지하지 못할 수 있으며, 이로 인해 일관성이 깨질 수 있습니다. 특히, 비동기 통신에서 문제가 발생할 경우에 대한 대응이 어려울 수 있습니다.
보상 트랜잭션의 대안
1. 이벤트 소싱 및 CQRS 패턴 활용
이벤트 소싱과 CQRS 패턴을 활용하여 데이터 변경을 이벤트로 기록하고, 이를 통해 필요한 보상 작업을 비동기적으로 수행할 수 있습니다.
2. Saga 패턴 적용
Saga 패턴은 여러 서비스 간의 트랜잭션을 처리하기 위한 패턴 중 하나입니다. 각 단계에서는 트랜잭션을 진행하거나 롤백하고, 상태를 추적하여 일관성을 유지합니다.
3. Compensation Log 사용
보상 트랜잭션 대신 각 트랜잭션의 성공 및 실패를 로깅하여, 실패 시에 해당 로그를 사용하여 보상 작업을 수행할 수 있습니다.
보상 트랜잭션 간단 예시
이 코드에서는 첫 번째 트랜잭션에서 잔액을 변경하는 작업과 주문 상태를 변경하는 작업을 수행합니다. 첫 번째 트랜잭션에서 문제가 발생하면 보상 트랜잭션에서는 해당 작업을 롤백하거나 복구합니다. 이는 단순한 시뮬레이션일 뿐이며, 실제 상황에서는 더 많은 예외 처리 및 로깅이 필요합니다.
public static void main(String[] args) {
try (Connection connection = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD)) {
try {
// 원본 트랜잭션에서 작업 수행
executeOperation(connection, "UPDATE account SET balance = balance - 100 WHERE user_id = 1");
executeOperation(connection, "UPDATE order SET status = 'FAILED' WHERE order_id = 123");
System.out.println("원본 트랜잭션 성공적으로 커밋됨.");
} catch (SQLException e) {
System.out.println("원본 트랜잭션 롤백.");
// 보상 작업 수행
try (Connection compensationConnection = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD)) {
executeOperation(compensationConnection, "UPDATE account SET balance = balance + 100 WHERE user_id = 1");
executeOperation(compensationConnection, "UPDATE order SET status = 'PENDING' WHERE order_id = 123");
System.out.println("보상 트랜잭션 성공적으로 커밋됨.");
} catch (SQLException ex) {
System.out.println("보상 트랜잭션 롤백.");
ex.printStackTrace();
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
private static void executeOperation(Connection connection, String query) throws SQLException {
try (PreparedStatement preparedStatement = connection.prepareStatement(query)) {
preparedStatement.executeUpdate();
}
}
'인프라 > MSA' 카테고리의 다른 글
[MSA] Axon Framewor (0) | 2023.12.22 |
---|---|
[MSA] 분산 트랜잭션 - 이벤트 소싱 (0) | 2023.12.20 |
[MSA] 분산 트랜잭션 - 2PC (0) | 2023.12.18 |
[MSA] 분산 트랜잭션 - ACID (1) | 2023.12.18 |
[MSA] Hexagonal Architecture (0) | 2023.12.07 |