FanUP은 비대면 팬미팅 플랫폼입니다.
해당 시리즈는 프로젝트를 진행하며 겪은 설계 고민, 트러블 슈팅 과정에 대해 다룹니다.
선착순 티켓팅 시리즈의 2번째 글입니다.
이전 글에서는 FanUP을 통해 사용자가 팬미팅에 참여하기까지의 대략적인 시나리오와 티켓팅 서비스의 기능 플로우에 대해 이야기 했습니다. 아직 이전 글을 읽지 않으셨다면 먼저 읽고 오시길 추천드립니다.
이번 글에서는 제목 그대로 티켓팅 기능을 구현해보고, 부하 테스트를 진행하여 결과를 확인해보겠습니다.
티켓 구매 요청의 경우 다음과 같은 플로우로 진행됩니다.
ticketId
유효성 검증 & 티켓 최대 수량(totalAmount
) 추출
userId
,ticketId
를 통해 중복 구매 여부 판단
- 티켓의 현재 판매 수량(
soldAmount
) 추출
- 티켓 구매 성공 여부 판단
4-1. 실패:totalAmount
보다soldAmount
가 크거나 같다면 티켓팅 실패,“All tickets are sold out”
exception 발생
4-2. 성공:UserTicket
테이블에 해당 (userId
,ticketId
) 정보 생성
- 성공한 요청에 한해 사용자 별로 방에 입장하기 위한
UUID
할당 -> Core Service 호출
아래는 앞서 작성한 플로우가 담겨있는 코드입니다.
티켓의 총 수량과 현재 판매된 티켓 수량을 비교하는 과정에서 동시성 이슈가 생길 것만 같은 코드네요...
async create(createUserTicketDto: CreateUserTicketDto): Promise<UserTicket> {
const userTicketResult = await this.prisma.$transaction(async (tx) => {
// 1: 티켓 유효성 검증 및 티켓 최대 수량 조회
const { totalAmount } = await tx.ticket.findUnique({
where: { id: createUserTicketDto.ticketId },
select: { totalAmount: true },
});
// TODO 2: 중복 구매 여부 판단
// 3: 현재 판매된 티켓 수량 조회
const soldAmount = await tx.userTicket.count({
where: {
ticketId: createUserTicketDto.ticketId,
},
});
// 4: 판매 수량과 최대 수량 비교, 구매 성공 여부 판단
if (soldAmount >= totalAmount) {
// 4-1. Exception 발생
throw new CustomRpcException(
'All tickets are sold out',
ResStatusCode.FORBIDDEN,
);
}
// 4-2. 구매에 성공한 사용자에 한해 티켓 구매 정보 생성
const userTicket = await tx.userTicket.create({
data: createUserTicketDto,
});
return userTicket;
});
// 5. fanUP 방 id 발급을 위한 event 전송 (외부 서비스 호출)
if (userTicketResult) {
this.event.emit('user-ticket.create', userTicketResult);
}
return userTicketResult;
}
티켓팅 기능의 경우 스파이크성 대량 트래픽이 유입되는 상황을 예상하여 설계 단계에서 cache
와 message queue
도입을 고려하였습니다.
하지만 처음부터 여러 기술을 적용하기 보다는 가장 기초가 되는 부분부터 점진적으로 고도화하며 성능이 얼마나 개선되는지를 확인해보고자 했습니다.
따라서 우선 RDB만을 사용하여 기능을 구현하고, 이에 대한 부하 테스트를 진행했습니다.
Artillery는 Node.js로 작성된 스트레스 테스트 도구입니다.
npm install -g artillery
명령어를 실행해 artillery 설치- yaml 형식의 테스트 스크립트 작성
artillery run —output report.json scenario.yaml
명령어를 실행해 테스트 진행artillery report report.json
명령어를 실행해 테스트 결과를html
파일로 생성
너무 흥미로운 내용이네요! 많이 배워갑니다^^