AWS Aurora를 사용하는 Spring Boot 애플리케이션에서 Writer 장애(Failover) 발생 시 트랜잭션을 유지하면서 서비스 중단을 최소화하려면 어떻게 해야 할까?
Aurora 클러스터에 연결된 상태에서 Writer 장애 발생 시 기존 커넥션이 유지되지 않으며, 트랜잭션 처리 방식은 autocommit
설정에 따라 다르게 동작한다.
이 글에서는 Aurora 장애 발생 시 트랜잭션이 어떻게 동작하는지와 애플리케이션에서 최적의 대응 방법을 정리한다. Aurora 장애 시 커넥션이 유지되는가?
오토 커밋(
autocommit
) 설정에 따라 트랜잭션 처리가 어떻게 달라지는가? Aurora 장애 대비 설정 방법 및 자동 복구를 위한 Retry 전략
1. 문제 상황
Spring Boot 애플리케이션이 HikariCP를 통해 Aurora 클러스터에 연결된 상태에서
Writer가 장애로 인해 다운되면 다음과 같은 문제가 발생할 수 있다.
일반적인 장애 발생 흐름
- Spring Boot 애플리케이션이 Aurora Writer와 연결된 상태에서 트랜잭션 실행 중
- Writer 장애 발생 → 새로운 Writer 승격
- 기존 Writer와 연결된 모든 커넥션이 폐기됨
- Spring Boot에서
CommunicationsException
또는Transaction rolled back because connection was closed
발생 - Retry 없이 처리하면 기존 트랜잭션이 롤백되지 않거나,
autocommit
설정에 따라 일부 데이터가 커밋됨 - HikariCP가 새로운 Writer로 재연결을 시도하지만, 기존 트랜잭션은 유지되지 않음
- Retry를 수행하면 새로운 트랜잭션을 시작하며 중복 데이터가 발생할 가능성이 있음
해결 목표:
- Aurora 장애 시에도 애플리케이션이 정상적으로 재시도하여 데이터 무결성을 유지해야 함.
- 커넥션 폐기 여부를 이해하고,
autocommit
설정에 따른 차이를 고려해야 함.
2. Aurora 장애 발생 시 기존 커넥션 유지 여부
Spring Boot에서 Aurora 클러스터에 **클러스터 엔드포인트 (jdbc:mysql:aurora://<cluster-endpoint>
)**를 사용하면,
Aurora가 자동으로 Writer를 승격해도 커넥션이 유지될 것처럼 보일 수 있다.
하지만 실제로 기존 Writer가 사라지면 해당 Writer와의 커넥션은 강제 종료되며, 기존 트랜잭션도 폐기됨.
Aurora 장애 시 커넥션 폐기 흐름
단계 | 상태 | 설명 |
---|---|---|
1. 정상 상태 | Writer와의 연결이 정상적으로 유지됨 | |
2. Writer 장애 발생 | Writer 다운으로 인해 기존 커넥션이 강제 폐기됨 | |
3. 새로운 Writer 승격 | 기존 Writer에서 진행되던 트랜잭션이 유지되지 않음 | |
4. HikariCP 재연결 | 새로운 Writer와 새 커넥션이 설정됨 | |
5. Retry 실행 | 기존 작업을 다시 수행함 |
Aurora 클러스터에 연결했다고 해서 기존 Writer 장애 시 기존 커넥션이 유지되는 것은 아니다!
커넥션 풀(HikariCP)은 장애 감지 후 새로운 Writer로 자동 연결하지만, 기존 트랜잭션은 유지되지 않는다.
3. Aurora 장애 대비 설정 (Failover Mode 변경)
Aurora는 기본적으로 페일오버 발생 시 세션을 유지하지 않음.
이를 방지하려면 Failover Mode 설정을 session
으로 변경해야 한다.
CALL mysql.rds_set_configuration('aurora_replica_read_consistency', 'session');
이 설정을 변경하면:
- Failover 발생 시 기존 트랜잭션이 최대한 유지될 수 있도록 보장.
- 하지만, Writer 장애 발생 시 기존 커넥션이 폐기되는 문제는 여전히 존재함.
4. autocommit
설정에 따른 트랜잭션 처리 차이
autocommit = true
(기본값)
- Aurora 장애 발생 전 실행된 SQL이 커밋됨.
- Retry 시 중복 데이터가 발생할 가능성이 높음.
Aurora 장애 발생 흐름 (
autocommit = true
)
INSERT INTO orders (id, name) VALUES (1, 'Order1'); --
즉시 커밋됨
-- 장애 발생
-- Retry 시 동일한 INSERT 실행 → 중복 데이터 발생 가능
autocommit = false
(@Transactional
사용)
- 장애 발생 시 트랜잭션이 폐기되며 롤백될 가능성이 큼.
- Retry 시 중복 데이터가 발생하지 않음.
Aurora 장애 발생 흐름 (
autocommit = false
)
@Transactional
public void processData() {
myRepository.save(new DataEntity()); // 장애 발생 시 롤백됨
}
트랜잭션이 유지되지 않으므로 장애 발생 시 롤백됨.
Retry 후 새로운 트랜잭션이 시작되므로 데이터 중복 문제가 없음.
5. Aurora 장애 대비 최적의 설정 방법
(1) autocommit = false
강제 적용 (@Transactional
)
@Service
public class MyService {
@Transactional
public void processData() {
myRepository.save(new DataEntity()); // 장애 발생 시 롤백됨 (중복 방지)
}
}
(2) RetryTemplate
을 활용한 자동 Retry
import org.springframework.retry.support.RetryTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class MyService {
private final RetryTemplate retryTemplate;
private final MyRepository myRepository;
public MyService(RetryTemplate retryTemplate, MyRepository myRepository) {
this.retryTemplate = retryTemplate;
this.myRepository = myRepository;
}
public void processData() {
retryTemplate.execute(context -> {
saveData();
return null;
});
}
@Transactional
public void saveData() {
myRepository.save(new DataEntity()); // 장애 발생 시 롤백됨
}
}
(3) AOP를 활용한 글로벌 Retry 적용
@Aspect
@Component
public class GlobalRetryAspect {
private final RetryTemplate retryTemplate;
public GlobalRetryAspect(RetryTemplate retryTemplate) {
this.retryTemplate = retryTemplate;
}
@Around("@annotation(org.springframework.transaction.annotation.Transactional)")
public Object retryTransactionalMethods(ProceedingJoinPoint joinPoint) throws Throwable {
return retryTemplate.execute(context -> {
return joinPoint.proceed();
});
}
}
6. 결론
Aurora 장애 발생 시 기존 커넥션은 폐기됨 (연결 유지 불가)
트랜잭션 유지 여부는
autocommit
설정에 따라 다르게 동작함. Retry 처리가 필수적이며,
@Transactional
을 통해 자동 롤백을 보장해야 함. AOP 기반 글로벌 Retry 적용으로 모든 트랜잭션에 일괄적으로 Retry 적용 가능.
Aurora 장애 발생 시 트랜잭션을 복구할 수 없으므로, 애플리케이션에서 자동으로 재시도하는 로직을 구현해야 한다!