티스토리 뷰
앞서 우리가 학습한 Transaction, Lock, Index, Cache 등은 모두 비즈니스 로직을 처리하는 데에 있어 중요한 Database의 부하를 줄이고, 속도 및 성능을 향상시키기 위함이었습니다. 그런데 Lock의 범위 뿐 아니라 무분별한 비즈니스 로직과 트랜잭션의 규모 또한 우리가 예측하지 못한 문제를 발생시킬 수 있습니다.
어떤 문제로 이어질 수 있을까?
- 다수의 SlowRead 작업으로 인해 요청 처리에 영향을 줄 수 있음
- Transaction 범위 내에서 Lock을 사용하고 있을 경우, 해당 자원에 접근하는 다른 요청의 대기 혹은 데드락 상황을 유발할 수 있음
- 긴 생명 주기의 Transaction의 경우, 오랜 시간은 소요되나 후속 작업에 의해 전체 트랜잭션이 실패할 수 있음
- DB 외적인 작업의 실패가 Transaction의 범위로 전파되어 전체 비즈니스 로직이 rollback되는 문제
- 만약, externalAPI가 실패하더라도 우리의 비즈니스는 정상적으로 성공시켜도 되는 요구사항이라면?
- externalAPI의 타임아웃으로 트랜잭션을 롤백시켰으나, external 서비스에서는 사실 정상적으로 처리 되었을 때 무결성을 잃게 되는 문제
위 문제들을 어떻게 해결할 수 있을까 고민해보세요.
Event 기반 흐름제어
- Event를 발행 및 구독하는 모델링을 통해 코드의 강항 결합을 분리함
- Event에 의해 본인의 관심사만 수행하도록 하여 비즈니스 로직간의 의존을 느슨하게 함
활용 방안
- 비대해진 트랜잭션 내의 각 작업을 작은 단위의 트랙잭션으로 분리할 수 있음
- 특정 작업이 완료되었을 떄, 후속 작업이 이벤트에 의해 triger되도록 구성함으로써 과도하게 많은 비즈니스 로직을 알고 있을 필요 없음
- 트랜잭션 내에서 외부 API 호출(e.g. DB 작업 등)의 실패나 수행이 주요 비즈니스 로직 (트랜잭션)에 영향을 주지 않도록 구성할 수 있음
동작 순서
1. service1 수행
2. service1 완료 이벤트 발행
3. service1 완료 이벤트에 대한 리스너가 본인의 비즈니스(service2_1 & service2_2) 수행
주의할 점
- 각 작업의 논리적 의존이나 관계를 잘 파악해야 함
- 만약 이벤트에 의해 파생된 작업이 실패하였을 때, 원본 작업 또한 실패 처리를 해야한다면 이를 위한 처리가 필요함 (keyword : 보상 트랜잭션, SAGA 패턴)
- 이벤트에 의해 각 작업이 영향을 주고 있는지, 혹은 이벤트가 발생하면 안되는 상황에서 이벤트가 발행되고 있지는 않은지 등
java 이벤트 예시
// 이벤트 객체
public class PaymentSuccessEvent {
private final String orderKey;
private final String paymentKey;
public PaymentSuccessEvent(String orderKey, String paymentKey) {
this.orderKey = orderKey;
this.paymentKey = paymentKey;
}
public String getOrderKey() {
return orderKey;
}
public String getPaymentKey() {
return paymentKey;
}
}
// 이벤트 발행서비스
@Component
public class PaymentEventPublisher {
private final ApplicationEventPublisher applicationEventPublisher;
public PaymentEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
public void success(PaymentSuccessEvent event) {
applicationEventPublisher.publishEvent(event);
}
}
// 이벤트 구독서비스
@Component
public class PaymentEventListener {
private final DataPlatformSendService sendService;
public PaymentEventListener(DataPlatformSendService sendService) {
this.sendService = sendService;
}
// 비동기로 이벤트 발행주체의 트랜잭션이 커밋된 후에 수행한다.
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void paymentSuccessHandler(PaymentSuccessEvent event) {
// (4) 주문 정보 전달
PaymentSuccessPayload payload = new PaymentSuccessPayload(event);
sendService.send(payload);
}
}
// 비즈니스 로직
@Service
public class PaymentService {
private final RequestValidator requestValidator;
private final UserService userService;
private final OrderService orderService;
private final PaymentRepository paymentRepository;
private final PaymentEventPublisher eventPublisher;
public PaymentService(RequestValidator requestValidator, UserService userService, OrderService orderService, PaymentRepository paymentRepository, PaymentEventPublisher eventPublisher) {
this.requestValidator = requestValidator;
this.userService = userService;
this.orderService = orderService;
this.paymentRepository = paymentRepository;
this.eventPublisher = eventPublisher;
}
@Transactional
public void pay(PaymentRequest request) {
// (1) 결제 요청 검증
requestValidator.validate(request);
// (2) 유저 포인트 차감
User user = userService.getWithLock(request.getUserId());
orderService.check(request.getOrderKey(), user.getId());
user.usePoint(request.getAmount());
// (3) 결제 정보 저장
Payment payment = paymentRepository.save(new Payment(request));
// 결제 성공 이벤트 발행
eventPublisher.success(new PaymentSuccessEvent(payment.getOrderKey(), payment.getKey()));
}
}
'향해99' 카테고리의 다른 글
이석범코치님 qna 트랙잭션과 관심사 분리를 위한 과정 (0) | 2024.08.07 |
---|---|
콘서트 예약시스템 - Index에 따른 속도 차이와 개선 보고서 (인덱싱 초보의 보고서 입니다. 검색으로 들어오신 분은 정확한 정보가 아닐 수 있으니 주의하세요!) (0) | 2024.08.06 |
챕터 3-3 8주차 부하를 적절하게 축소하기 - 인덱스 (index) (0) | 2024.08.06 |
STEP 13.콘서트 트래픽 처리 + 토큰 전략 (향해99 백엔드 5기) (0) | 2024.07.31 |
STEP 13.콘서트 트래픽 처리 +캐싱 전략 (향해99 백엔드 5기) (0) | 2024.07.31 |
- Total
- Today
- Yesterday
- 스프링부트
- jpa api
- 리터럴
- 로그인
- ArgumentResolver
- reject
- react실행
- HTTP
- 백엔드 개발자 공부
- 인터셉터
- BindingResult
- thymleaf
- rejectValue
- hypertexttransferprotocol
- 향해플러스
- 백엔드 개발자 역량
- Intercepter
- JPA
- Java
- 향해99
- exception
- 항해99
- React
- 스프링공부
- 항해플러스
- SpringBoot
- 예외처리
- 컨트
- filter
- 향해플러스백엔드
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |