연결 장애·서비스별 복구와 성공 커밋의 보존
같은 연결 장애 뒤 업무 복구·커밋 보존·재연결 부담은?
결과 요약
이 실험은 애플리케이션과 DB 사이의 연결이 잠시 끊겼을 때, 연결이 돌아온 뒤 정상 처리로 복귀하는지와 이미 성공한 커밋이 사라지거나 두 번 반영되지 않는지를 확인했습니다. 최소 범위(MVP)로, 초당 300건의 주문 업무를 보내는 도중 부하 발생기에서 DB로 가는 패킷을 30초 동안 버리도록 경로를 막았습니다(블랙홀 라우트). 이것은 클라이언트 쪽 네트워크 장애이며, DB 자체의 장애나 장애 조치(failover)가 아닙니다.
DSQL(D1)과 Aurora Serverless v2(A2) 모두 차단된 30초 동안의 요청이 연결 오류와 시간 초과로 실패해, 150초 측정 구간 전체의 기술적 실패율이 D1 20.6%, A2 20.7%였습니다. 차단 시간이 측정 구간의 20%이므로 차단 구간 요청은 대부분 실패하고 나머지 구간은 정상 처리된 것으로 해석합니다. 차단을 푼 뒤 새 연결로 첫 조회가 성공하기까지 D1은 0.02초, A2는 0.07초였습니다.
가장 중요한 결과는 정합성입니다. 커밋 응답을 받지 못해 성공 여부가 불명확한 요청은 업무 ID 영수증으로 확인한 뒤 재시도하도록 구현했고, 셀이 끝난 뒤 영수증·주문·원장·재고를 대조했습니다. 두 서비스 모두 커밋 유실, 이중 반영, 재고 불일치가 0건이었습니다. DSQL도 대조군과 같은 방식(업무 ID 영수증과 커밋 여부 확인)을 구현하면 연결이 끊긴 상황에서 정합성을 지킬 수 있었습니다.
프로덕션 사용 관점
이번 시험 범위에서 DSQL은 연결이 30초 끊겨도 성공한 커밋을 잃거나 두 번 반영하지 않았고, 연결이 돌아오면 바로 다시 연결했습니다. 다만 이것은 애플리케이션이 업무 ID 영수증과 “커밋 여부 불명확 시 확인 후 재시도”를 구현했기 때문이며, 이 구현은 DSQL과 기존 서비스 모두에 필요합니다. 차단 동안의 요청은 두 서비스 모두 실패했으므로 복구 시간은 네트워크가 돌아오는 시간에 좌우됩니다. DSQL 내부 장애나 AZ 장애에서 어떻게 동작하는지는 공개된 장애 주입 수단이 없어 이번에 확인하지 못했고, Aurora의 장애 조치 시간도 측정하지 않았습니다.
확인할 질문
같은 연결 장애 뒤 업무 복구·커밋 보존·재연결 부담은? 이번 MVP는 공통 클라이언트 연결 장애만 다룹니다.
실행 조건
- 리전과 일시: 서울(ap-northeast-2), 2026-09-29. A2 08:40–08:43 UTC, D1 10:00–10:04 UTC(D1 첫 시도는 측정 도구 결함으로 다시 실행).
- 구성: D1 Aurora DSQL 단일 리전, A2 Aurora PostgreSQL 16.15 Serverless v2(writer+reader, 4–32 ACU). 데이터는 E002 규모의 2%.
- 부하: E002와 같은 업무 혼합(상품 조회 40%, 주문 이력 30%, 주문 생성 20%, 취소 10%)을 고정 도착률 300 TPS로 보냈습니다. 워밍업 30초 + 측정 150초, 측정 시작 40초 뒤부터 30초 동안 차단했습니다.
- 장애 주입: 러너에서 DB 엔드포인트가 가리키는 IP로 가는 경로를
ip route add blackhole로 막고 30초 뒤 지웠습니다(D1은 IP 2개, A2는 1개). - 재시도: 요청당 최대 3회, 총 마감 2초. 커밋 응답을 받지 못한 요청은 업무 ID 영수증을 조회해 커밋 여부를 확인한 뒤 재시도합니다.
- 정합성 검사: 셀이 만든 행만 골라 영수증 수가 클라이언트가 확인한 커밋 수와 불명확 건수 범위 안에 있는지, 주문·원장·재고 변화가 영수증과 맞는지 확인했습니다.
- 부하 발생기: D1
c6g.4xlarge, A2m7g.4xlargeSpot 러너. - 계획과 달라진 점: Q의 30%/80% 두 부하, 조건당 3회, 차단 전후 10분 관측, 서비스별 장애 조치(RDS reboot with failover, Aurora cluster failover)는 실행하지 않았습니다.
성능 결과
| 구성 | 시도 TPS | 성공 TPS | 기술적 실패율 | 차단 해제 후 첫 연결 성공 | 불변식 위반 |
|---|---|---|---|---|---|
| D1 | 289.6 | 225.6 | 20.6% | 0.02초 | 0건 |
| A2 | 292.4 | 237.6 | 20.7% | 0.07초 | 0건 |
- 실패 대부분은 연결 오류였습니다(D1 주문 생성 실패 1,614건 중 1,572건, A2 1,663건 중 1,619건). 나머지는 2초 마감 초과와 소수의 직렬화 충돌이었습니다.
- 차단 구간이 섞인 셀이라 p99는 두 서비스 모두 1.5–1.9초로 커졌습니다. 차단 밖 구간의 지연은 E002 결과와 같은 수준입니다(D1 쓰기 p95 약 29–30 ms, A2 약 9 ms).
- D1의 취소 요청 가운데 1,693건이 “이미 취소된 주문”으로 거절되었습니다. 앞선 E009 시험들이 같은 데이터에서 취소를 누적했기 때문이며, 정합성 판정에는 영향이 없습니다.
개발·운영 편의성
- 두 서비스 모두 연결 풀이 끊긴 연결을 버리고 새 연결을 맺는 것만으로 복구되었고, DSQL에만 필요한 추가 작업은 없었습니다. DSQL은 새 연결마다 IAM 토큰이 필요하지만 토큰은 10분 캐시해 재연결 지연에 거의 영향을 주지 않았습니다.
- 측정 도구 결함: D1 첫 시도에서 셀 시작 전에 열어 둔 관리용 연결이 차단으로 끊겨 셀 뒤 정합성 검사가 실패했습니다. 셀이 끝난 뒤 새 연결로 검사하도록 고치고 다시 측정했습니다.
비용
실행 B 전체 비용은 E009 보고서의 비용 절에 정리했습니다. 이 시험은 구성당 약 3분 부하였습니다.
결론과 한계
- 클라이언트 쪽 30초 연결 장애에서 DSQL과 A2는 같은 결과를 보였습니다: 차단 구간 요청 실패, 해제 직후 재연결, 커밋 유실·중복 0건.
- 한계: 1회 실행, 소규모 데이터, 낮은 부하(300 TPS)입니다. 이번 1회 실행에서 유실·중복이 없었다는 결과를 절대적 보증으로 일반화하지 않습니다. DB 장애 조치와 DSQL 내부 장애는 다루지 않았습니다.
정리 기록
- 실행 B 생성 자원: BATCH(VPC, 서브넷 2개, IGW, 보안 그룹 2개, DB 서브넷 그룹, 러너 IAM 역할·인스턴스 프로파일), D1 DSQL 클러스터, A2 클러스터와 writer·reader, RDS 관리 시크릿, Spot 러너 2대(D1
c6g.4xlarge, A2m7g.4xlarge). E007에서 AWS Backup 볼트, 백업 서비스 역할, 복구 지점 2개, 복원된 DSQL 클러스터 1개, 시점 복원한 A2 클러스터와 인스턴스를 추가로 만들었습니다. - 삭제: 백업 볼트와 복구 지점 2개는 09:35 UTC, 복원된 DSQL 클러스터와 A2 복원본은 09:48 UTC부터 약 10:05 UTC까지 소유 태그를 확인한 뒤 삭제했습니다. 나머지는
e002.py batch-down으로 10:04–10:20 UTC에 삭제했습니다. 삭제에 실패한 자원은 없습니다. - 검증 (10:20:27 UTC):
e002.py verify의remaining_count=0입니다. 수동 교차 확인으로 AWS Backup 볼트 0개, 이 실행 접두어의 RDS 클러스터·IAM 역할 0개, 열린 Spot 요청 0개를 확인했습니다. 이 시각에 남아 있던 DSQL 클러스터 1개는 동시에 실행 중이던 실행 A(E002·E003·E008·E012)의 것입니다.