[선착순 티켓팅] 2. 기능 구현과 부하 테스트

소포카·2022년 12월 11일
2

FanUP

목록 보기
3/4
post-thumbnail

FanUP은 비대면 팬미팅 플랫폼입니다.
해당 시리즈는 프로젝트를 진행하며 겪은 설계 고민, 트러블 슈팅 과정에 대해 다룹니다.

Github: https://github.com/boostcampwm-2022/web03-FanUP

개요

선착순 티켓팅 시리즈의 2번째 글입니다.
이전 글에서는 FanUP을 통해 사용자가 팬미팅에 참여하기까지의 대략적인 시나리오와 티켓팅 서비스의 기능 플로우에 대해 이야기 했습니다. 아직 이전 글을 읽지 않으셨다면 먼저 읽고 오시길 추천드립니다.

이번 글에서는 제목 그대로 티켓팅 기능을 구현해보고, 부하 테스트를 진행하여 결과를 확인해보겠습니다.

기능 구현

코드 작성

티켓 구매 요청의 경우 다음과 같은 플로우로 진행됩니다.

  1. ticketId 유효성 검증 & 티켓 최대 수량(totalAmount) 추출
  1. userId, ticketId를 통해 중복 구매 여부 판단
  1. 티켓의 현재 판매 수량(soldAmount) 추출
  1. 티켓 구매 성공 여부 판단
    4-1. 실패: totalAmount보다 soldAmount가 크거나 같다면 티켓팅 실패, “All tickets are sold out” exception 발생
    4-2. 성공: UserTicket 테이블에 해당 (userId, ticketId) 정보 생성
  1. 성공한 요청에 한해 사용자 별로 방에 입장하기 위한 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;
}

성능 측정

티켓팅 기능의 경우 스파이크성 대량 트래픽이 유입되는 상황을 예상하여 설계 단계에서 cachemessage queue 도입을 고려하였습니다.
하지만 처음부터 여러 기술을 적용하기 보다는 가장 기초가 되는 부분부터 점진적으로 고도화하며 성능이 얼마나 개선되는지를 확인해보고자 했습니다.

따라서 우선 RDB만을 사용하여 기능을 구현하고, 이에 대한 부하 테스트를 진행했습니다.

Artillery

Artillery는 Node.js로 작성된 스트레스 테스트 도구입니다.

  1. npm install -g artillery 명령어를 실행해 artillery 설치
  2. yaml 형식의 테스트 스크립트 작성
  3. artillery run —output report.json scenario.yaml 명령어를 실행해 테스트 진행
  4. artillery report report.json 명령어를 실행해 테스트 결과를 html 파일로 생성
profile
https://github.com/sophoca

2개의 댓글

comment-user-thumbnail
2023년 4월 19일

너무 흥미로운 내용이네요! 많이 배워갑니다^^

1개의 답글